ruby-lsp 0.23.12 → 0.23.13

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +31 -40
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +56 -22
  6. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -1
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +15 -18
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -11
  9. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +2 -2
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +1 -1
  11. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +2 -2
  12. data/lib/ruby_indexer/test/configuration_test.rb +10 -1
  13. data/lib/ruby_indexer/test/method_test.rb +26 -0
  14. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  15. data/lib/ruby_lsp/addon.rb +12 -4
  16. data/lib/ruby_lsp/base_server.rb +19 -22
  17. data/lib/ruby_lsp/client_capabilities.rb +6 -6
  18. data/lib/ruby_lsp/document.rb +13 -13
  19. data/lib/ruby_lsp/erb_document.rb +7 -9
  20. data/lib/ruby_lsp/global_state.rb +21 -24
  21. data/lib/ruby_lsp/listeners/code_lens.rb +59 -48
  22. data/lib/ruby_lsp/listeners/completion.rb +2 -2
  23. data/lib/ruby_lsp/listeners/definition.rb +2 -2
  24. data/lib/ruby_lsp/listeners/document_highlight.rb +62 -80
  25. data/lib/ruby_lsp/listeners/document_link.rb +34 -43
  26. data/lib/ruby_lsp/listeners/document_symbol.rb +1 -1
  27. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  28. data/lib/ruby_lsp/listeners/hover.rb +43 -52
  29. data/lib/ruby_lsp/listeners/inlay_hints.rb +1 -1
  30. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +11 -14
  31. data/lib/ruby_lsp/listeners/signature_help.rb +2 -2
  32. data/lib/ruby_lsp/listeners/spec_style.rb +15 -59
  33. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  34. data/lib/ruby_lsp/listeners/test_style.rb +47 -81
  35. data/lib/ruby_lsp/node_context.rb +4 -4
  36. data/lib/ruby_lsp/rbs_document.rb +1 -1
  37. data/lib/ruby_lsp/requests/code_actions.rb +9 -12
  38. data/lib/ruby_lsp/requests/code_lens.rb +2 -4
  39. data/lib/ruby_lsp/requests/completion.rb +3 -5
  40. data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
  41. data/lib/ruby_lsp/requests/definition.rb +4 -5
  42. data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
  43. data/lib/ruby_lsp/requests/discover_tests.rb +3 -2
  44. data/lib/ruby_lsp/requests/document_highlight.rb +2 -4
  45. data/lib/ruby_lsp/requests/document_link.rb +2 -4
  46. data/lib/ruby_lsp/requests/document_symbol.rb +1 -1
  47. data/lib/ruby_lsp/requests/folding_ranges.rb +3 -8
  48. data/lib/ruby_lsp/requests/formatting.rb +2 -2
  49. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +3 -3
  50. data/lib/ruby_lsp/requests/hover.rb +2 -2
  51. data/lib/ruby_lsp/requests/inlay_hints.rb +2 -4
  52. data/lib/ruby_lsp/requests/on_type_formatting.rb +10 -13
  53. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  54. data/lib/ruby_lsp/requests/range_formatting.rb +2 -2
  55. data/lib/ruby_lsp/requests/references.rb +1 -1
  56. data/lib/ruby_lsp/requests/rename.rb +2 -2
  57. data/lib/ruby_lsp/requests/request.rb +2 -2
  58. data/lib/ruby_lsp/requests/selection_ranges.rb +2 -2
  59. data/lib/ruby_lsp/requests/semantic_highlighting.rb +5 -7
  60. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
  61. data/lib/ruby_lsp/requests/signature_help.rb +2 -2
  62. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  63. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +15 -21
  64. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  65. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -18
  66. data/lib/ruby_lsp/requests/support/sorbet.rb +28 -31
  67. data/lib/ruby_lsp/requests/support/source_uri.rb +11 -14
  68. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +5 -9
  69. data/lib/ruby_lsp/requests/support/test_item.rb +4 -9
  70. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  71. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -1
  72. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -1
  73. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -5
  74. data/lib/ruby_lsp/response_builders/hover.rb +5 -8
  75. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +41 -47
  76. data/lib/ruby_lsp/response_builders/signature_help.rb +1 -1
  77. data/lib/ruby_lsp/response_builders/test_collection.rb +1 -1
  78. data/lib/ruby_lsp/ruby_document.rb +7 -20
  79. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +7 -4
  80. data/lib/ruby_lsp/scope.rb +1 -1
  81. data/lib/ruby_lsp/server.rb +9 -1
  82. data/lib/ruby_lsp/setup_bundler.rb +36 -42
  83. data/lib/ruby_lsp/static_docs.rb +4 -7
  84. data/lib/ruby_lsp/store.rb +9 -12
  85. data/lib/ruby_lsp/test_reporter.rb +140 -4
  86. data/lib/ruby_lsp/test_unit_test_runner.rb +10 -8
  87. data/lib/ruby_lsp/utils.rb +10 -16
  88. metadata +3 -2
@@ -6,55 +6,49 @@ module RubyLsp
6
6
  class Hover
7
7
  include Requests::Support::Common
8
8
 
9
- ALLOWED_TARGETS = T.let(
10
- [
11
- Prism::CallNode,
12
- Prism::ConstantReadNode,
13
- Prism::ConstantWriteNode,
14
- Prism::ConstantPathNode,
15
- Prism::GlobalVariableAndWriteNode,
16
- Prism::GlobalVariableOperatorWriteNode,
17
- Prism::GlobalVariableOrWriteNode,
18
- Prism::GlobalVariableReadNode,
19
- Prism::GlobalVariableTargetNode,
20
- Prism::GlobalVariableWriteNode,
21
- Prism::InstanceVariableReadNode,
22
- Prism::InstanceVariableAndWriteNode,
23
- Prism::InstanceVariableOperatorWriteNode,
24
- Prism::InstanceVariableOrWriteNode,
25
- Prism::InstanceVariableTargetNode,
26
- Prism::InstanceVariableWriteNode,
27
- Prism::SymbolNode,
28
- Prism::StringNode,
29
- Prism::InterpolatedStringNode,
30
- Prism::SuperNode,
31
- Prism::ForwardingSuperNode,
32
- Prism::YieldNode,
33
- Prism::ClassVariableAndWriteNode,
34
- Prism::ClassVariableOperatorWriteNode,
35
- Prism::ClassVariableOrWriteNode,
36
- Prism::ClassVariableReadNode,
37
- Prism::ClassVariableTargetNode,
38
- Prism::ClassVariableWriteNode,
39
- ],
40
- T::Array[T.class_of(Prism::Node)],
41
- )
42
-
43
- ALLOWED_REMOTE_PROVIDERS = T.let(
44
- [
45
- "https://github.com",
46
- "https://gitlab.com",
47
- ].freeze,
48
- T::Array[String],
49
- )
9
+ ALLOWED_TARGETS = [
10
+ Prism::CallNode,
11
+ Prism::ConstantReadNode,
12
+ Prism::ConstantWriteNode,
13
+ Prism::ConstantPathNode,
14
+ Prism::GlobalVariableAndWriteNode,
15
+ Prism::GlobalVariableOperatorWriteNode,
16
+ Prism::GlobalVariableOrWriteNode,
17
+ Prism::GlobalVariableReadNode,
18
+ Prism::GlobalVariableTargetNode,
19
+ Prism::GlobalVariableWriteNode,
20
+ Prism::InstanceVariableReadNode,
21
+ Prism::InstanceVariableAndWriteNode,
22
+ Prism::InstanceVariableOperatorWriteNode,
23
+ Prism::InstanceVariableOrWriteNode,
24
+ Prism::InstanceVariableTargetNode,
25
+ Prism::InstanceVariableWriteNode,
26
+ Prism::SymbolNode,
27
+ Prism::StringNode,
28
+ Prism::InterpolatedStringNode,
29
+ Prism::SuperNode,
30
+ Prism::ForwardingSuperNode,
31
+ Prism::YieldNode,
32
+ Prism::ClassVariableAndWriteNode,
33
+ Prism::ClassVariableOperatorWriteNode,
34
+ Prism::ClassVariableOrWriteNode,
35
+ Prism::ClassVariableReadNode,
36
+ Prism::ClassVariableTargetNode,
37
+ Prism::ClassVariableWriteNode,
38
+ ] #: Array[singleton(Prism::Node)]
39
+
40
+ ALLOWED_REMOTE_PROVIDERS = [
41
+ "https://github.com",
42
+ "https://gitlab.com",
43
+ ].freeze #: Array[String]
50
44
 
51
45
  #: (ResponseBuilders::Hover response_builder, GlobalState global_state, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher, RubyDocument::SorbetLevel sorbet_level) -> void
52
46
  def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
53
47
  @response_builder = response_builder
54
48
  @global_state = global_state
55
- @index = T.let(global_state.index, RubyIndexer::Index)
56
- @type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
57
- @path = T.let(uri.to_standardized_path, T.nilable(String))
49
+ @index = global_state.index #: RubyIndexer::Index
50
+ @type_inferrer = global_state.type_inferrer #: TypeInferrer
51
+ @path = uri.to_standardized_path #: String?
58
52
  @node_context = node_context
59
53
  @sorbet_level = sorbet_level
60
54
 
@@ -387,14 +381,11 @@ module RubyLsp
387
381
  spec = Gem::Specification.find_by_name(first_argument.content)
388
382
  return unless spec
389
383
 
390
- info = T.let(
391
- [
392
- spec.description,
393
- spec.summary,
394
- "This rubygem does not have a description or summary.",
395
- ].find { |text| !text.nil? && !text.empty? },
396
- String,
397
- )
384
+ info = [
385
+ spec.description,
386
+ spec.summary,
387
+ "This rubygem does not have a description or summary.",
388
+ ].find { |text| !text.nil? && !text.empty? } #: String
398
389
 
399
390
  # Remove leading whitespace if a heredoc was used for the summary or description
400
391
  info = info.gsub(/^ +/, "")
@@ -6,7 +6,7 @@ module RubyLsp
6
6
  class InlayHints
7
7
  include Requests::Support::Common
8
8
 
9
- RESCUE_STRING_LENGTH = T.let("rescue".length, Integer)
9
+ RESCUE_STRING_LENGTH = "rescue".length #: Integer
10
10
 
11
11
  #: (ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint] response_builder, RequestConfig hints_configuration, Prism::Dispatcher dispatcher) -> void
12
12
  def initialize(response_builder, hints_configuration, dispatcher)
@@ -6,24 +6,21 @@ module RubyLsp
6
6
  class SemanticHighlighting
7
7
  include Requests::Support::Common
8
8
 
9
- SPECIAL_RUBY_METHODS = T.let(
10
- [
11
- Module.instance_methods(false),
12
- Kernel.instance_methods(false),
13
- Kernel.methods(false),
14
- Bundler::Dsl.instance_methods(false),
15
- Module.private_instance_methods(false),
16
- ].flatten.map(&:to_s).freeze,
17
- T::Array[String],
18
- )
9
+ SPECIAL_RUBY_METHODS = [
10
+ Module.instance_methods(false),
11
+ Kernel.instance_methods(false),
12
+ Kernel.methods(false),
13
+ Bundler::Dsl.instance_methods(false),
14
+ Module.private_instance_methods(false),
15
+ ].flatten.map(&:to_s).freeze #: Array[String]
19
16
 
20
17
  #: (Prism::Dispatcher dispatcher, ResponseBuilders::SemanticHighlighting response_builder) -> void
21
18
  def initialize(dispatcher, response_builder)
22
19
  @response_builder = response_builder
23
- @special_methods = T.let(nil, T.nilable(T::Array[String]))
24
- @current_scope = T.let(Scope.new, Scope)
25
- @inside_regex_capture = T.let(false, T::Boolean)
26
- @inside_implicit_node = T.let(false, T::Boolean)
20
+ @special_methods = nil #: Array[String]?
21
+ @current_scope = Scope.new #: Scope
22
+ @inside_regex_capture = false #: bool
23
+ @inside_implicit_node = false #: bool
27
24
 
28
25
  dispatcher.register(
29
26
  self,
@@ -11,8 +11,8 @@ module RubyLsp
11
11
  @sorbet_level = sorbet_level
12
12
  @response_builder = response_builder
13
13
  @global_state = global_state
14
- @index = T.let(global_state.index, RubyIndexer::Index)
15
- @type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
14
+ @index = global_state.index #: RubyIndexer::Index
15
+ @type_inferrer = global_state.type_inferrer #: TypeInferrer
16
16
  @node_context = node_context
17
17
  dispatcher.register(self, :on_call_node_enter)
18
18
  end
@@ -3,28 +3,20 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class SpecStyle
6
+ class SpecStyle < TestDiscovery
7
7
  extend T::Sig
8
- include Requests::Support::Common
9
-
10
- DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>"
11
8
 
12
9
  #: (response_builder: ResponseBuilders::TestCollection, global_state: GlobalState, dispatcher: Prism::Dispatcher, uri: URI::Generic) -> void
13
10
  def initialize(response_builder, global_state, dispatcher, uri)
14
- @response_builder = response_builder
15
- @uri = uri
16
- @index = T.let(global_state.index, RubyIndexer::Index)
17
- @visibility_stack = T.let([:public], T::Array[Symbol])
18
- @nesting = T.let([], T::Array[String])
19
- @describe_block_nesting = T.let([], T::Array[String])
20
- @spec_class_stack = T.let([], T::Array[T::Boolean])
11
+ super
12
+
13
+ @describe_block_nesting = [] #: Array[String]
14
+ @spec_class_stack = [] #: Array[bool]
21
15
 
22
16
  dispatcher.register(
23
17
  self,
18
+ # Common handlers registered in parent class
24
19
  :on_class_node_enter,
25
- :on_class_node_leave,
26
- :on_module_node_enter,
27
- :on_module_node_leave,
28
20
  :on_call_node_enter, # e.g. `describe` or `it`
29
21
  :on_call_node_leave,
30
22
  )
@@ -32,46 +24,16 @@ module RubyLsp
32
24
 
33
25
  #: (node: Prism::ClassNode) -> void
34
26
  def on_class_node_enter(node)
35
- @visibility_stack << :public
36
- name = constant_name(node.constant_path)
37
- name ||= name_with_dynamic_reference(node.constant_path)
38
-
39
- fully_qualified_name = RubyIndexer::Index.actual_nesting(@nesting, name).join("::")
40
-
41
- attached_ancestors = begin
42
- @index.linearized_ancestors_of(fully_qualified_name)
43
- rescue RubyIndexer::Index::NonExistingNamespaceError
44
- # When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
45
- # provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
46
- [node.superclass&.slice].compact
27
+ with_test_ancestor_tracking(node) do |_, ancestors|
28
+ is_spec = ancestors.include?("Minitest::Spec")
29
+ @spec_class_stack.push(is_spec)
47
30
  end
48
-
49
- is_spec = attached_ancestors.include?("Minitest::Spec")
50
- @spec_class_stack.push(is_spec)
51
-
52
- @nesting << name
53
- end
54
-
55
- #: (node: Prism::ModuleNode) -> void
56
- def on_module_node_enter(node)
57
- @visibility_stack << :public
58
-
59
- name = constant_name(node.constant_path)
60
- name ||= name_with_dynamic_reference(node.constant_path)
61
-
62
- @nesting << name
63
- end
64
-
65
- #: (node: Prism::ModuleNode) -> void
66
- def on_module_node_leave(node)
67
- @visibility_stack.pop
68
- @nesting.pop
69
31
  end
70
32
 
71
33
  #: (node: Prism::ClassNode) -> void
72
- def on_class_node_leave(node)
73
- @visibility_stack.pop
74
- @nesting.pop
34
+ def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
35
+ super
36
+
75
37
  @spec_class_stack.pop
76
38
  end
77
39
 
@@ -109,7 +71,7 @@ module RubyLsp
109
71
  description,
110
72
  @uri,
111
73
  range_from_node(node),
112
- tags: [:minitest],
74
+ framework: :minitest,
113
75
  )
114
76
  @response_builder.add(test_item)
115
77
  else
@@ -141,7 +103,7 @@ module RubyLsp
141
103
  description,
142
104
  @uri,
143
105
  range_from_node(node),
144
- tags: [:minitest],
106
+ framework: :minitest,
145
107
  )
146
108
  parent_test_group.add(test_item)
147
109
  end
@@ -155,7 +117,7 @@ module RubyLsp
155
117
  end
156
118
  return unless root_group_name
157
119
 
158
- test_group = T.let(@response_builder[root_group_name], T.nilable(Requests::Support::TestItem))
120
+ test_group = @response_builder[root_group_name] #: Requests::Support::TestItem?
159
121
  return unless test_group
160
122
 
161
123
  return test_group unless nested_describe_groups
@@ -188,12 +150,6 @@ module RubyLsp
188
150
 
189
151
  T.must(@spec_class_stack.last)
190
152
  end
191
-
192
- #: (node: Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) -> String
193
- def name_with_dynamic_reference(node)
194
- slice = node.slice
195
- slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
196
- end
197
153
  end
198
154
  end
199
155
  end
@@ -0,0 +1,89 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Listeners
6
+ class TestDiscovery
7
+ extend T::Helpers
8
+ abstract!
9
+
10
+ include Requests::Support::Common
11
+
12
+ DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>"
13
+
14
+ #: (ResponseBuilders::TestCollection response_builder, GlobalState global_state, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
15
+ def initialize(response_builder, global_state, dispatcher, uri)
16
+ @response_builder = response_builder
17
+ @uri = uri
18
+ @index = global_state.index #: RubyIndexer::Index
19
+ @visibility_stack = [:public] #: Array[Symbol]
20
+ @nesting = [] #: Array[String]
21
+
22
+ dispatcher.register(
23
+ self,
24
+ :on_class_node_leave,
25
+ :on_module_node_enter,
26
+ :on_module_node_leave,
27
+ )
28
+ end
29
+
30
+ #: (Prism::ModuleNode node) -> void
31
+ def on_module_node_enter(node)
32
+ @visibility_stack << :public
33
+
34
+ name = constant_name(node.constant_path)
35
+ name ||= name_with_dynamic_reference(node.constant_path)
36
+
37
+ @nesting << name
38
+ end
39
+
40
+ #: (Prism::ModuleNode node) -> void
41
+ def on_module_node_leave(node)
42
+ @visibility_stack.pop
43
+ @nesting.pop
44
+ end
45
+
46
+ #: (Prism::ClassNode node) -> void
47
+ def on_class_node_leave(node)
48
+ @visibility_stack.pop
49
+ @nesting.pop
50
+ end
51
+
52
+ private
53
+
54
+ #: (String? name) -> String
55
+ def calc_fully_qualified_name(name)
56
+ RubyIndexer::Index.actual_nesting(@nesting, name).join("::")
57
+ end
58
+
59
+ #: (Prism::ClassNode node, String fully_qualified_name) -> Array[String]
60
+ def calc_attached_ancestors(node, fully_qualified_name)
61
+ @index.linearized_ancestors_of(fully_qualified_name)
62
+ rescue RubyIndexer::Index::NonExistingNamespaceError
63
+ # When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
64
+ # provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
65
+ [node.superclass&.slice].compact
66
+ end
67
+
68
+ #: (Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode node) -> String
69
+ def name_with_dynamic_reference(node)
70
+ slice = node.slice
71
+ slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
72
+ end
73
+
74
+ #: (Prism::ClassNode node, ^(String name, Array[String] ancestors) -> void block) -> void
75
+ def with_test_ancestor_tracking(node, &block)
76
+ @visibility_stack << :public
77
+ name = constant_name(node.constant_path)
78
+ name ||= name_with_dynamic_reference(node.constant_path)
79
+
80
+ fully_qualified_name = calc_fully_qualified_name(name)
81
+ attached_ancestors = calc_attached_ancestors(node, fully_qualified_name)
82
+
83
+ block.call(fully_qualified_name, attached_ancestors)
84
+
85
+ @nesting << name
86
+ end
87
+ end
88
+ end
89
+ end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class TestStyle
6
+ class TestStyle < TestDiscovery
7
7
  class << self
8
8
  # Resolves the minimal set of commands required to execute the requested tests
9
9
  #: (Array[Hash[Symbol, untyped]]) -> Array[String]
@@ -24,6 +24,7 @@ module RubyLsp
24
24
  until queue.empty?
25
25
  item = T.must(queue.shift)
26
26
  tags = Set.new(item[:tags])
27
+ next unless tags.include?("framework:minitest") || tags.include?("framework:test_unit")
27
28
 
28
29
  children = item[:children]
29
30
  uri = URI(item[:uri])
@@ -31,7 +32,12 @@ module RubyLsp
31
32
  next unless path
32
33
 
33
34
  if tags.include?("test_dir")
34
- full_files << "#{path}/**/*" if children.empty?
35
+ if children.empty?
36
+ full_files.concat(Dir.glob(
37
+ "#{path}/**/{*_test,test_*}.rb",
38
+ File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
39
+ ))
40
+ end
35
41
  elsif tags.include?("test_file")
36
42
  full_files << path if children.empty?
37
43
  elsif tags.include?("test_group")
@@ -40,7 +46,7 @@ module RubyLsp
40
46
  unless children.any? && children.all? { |child| child[:tags].include?("test_group") }
41
47
  aggregated_tests[path][item[:label]] = { tags: tags, examples: [] }
42
48
  end
43
- elsif tags.include?("minitest") || tags.include?("test_unit")
49
+ else
44
50
  class_name, method_name = item[:id].split("#")
45
51
  aggregated_tests[path][class_name][:examples] << method_name
46
52
  aggregated_tests[path][class_name][:tags].merge(tags)
@@ -55,7 +61,7 @@ module RubyLsp
55
61
  # Separate groups into Minitest and Test Unit. You can have both frameworks in the same file, but you cannot
56
62
  # have a group belongs to both at the same time
57
63
  minitest_groups, test_unit_groups = groups_and_examples.partition do |_, info|
58
- info[:tags].include?("minitest")
64
+ info[:tags].include?("framework:minitest")
59
65
  end
60
66
 
61
67
  if minitest_groups.any?
@@ -67,7 +73,10 @@ module RubyLsp
67
73
  end
68
74
  end
69
75
 
70
- commands << "#{BASE_COMMAND} -Itest #{full_files.join(" ")}" unless full_files.empty?
76
+ unless full_files.empty?
77
+ commands << "#{BASE_COMMAND} -Itest -e \"ARGV.each { |f| require f }\" #{full_files.join(" ")}"
78
+ end
79
+
71
80
  commands
72
81
  end
73
82
 
@@ -77,7 +86,10 @@ module RubyLsp
77
86
  def handle_minitest_groups(file_path, groups_and_examples)
78
87
  regexes = groups_and_examples.flat_map do |group, info|
79
88
  examples = info[:examples]
80
- group_regex = Shellwords.escape(group).gsub(Shellwords.escape(DYNAMIC_REFERENCE_MARKER), ".*")
89
+ group_regex = Shellwords.escape(group).gsub(
90
+ Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
91
+ ".*",
92
+ )
81
93
  if examples.empty?
82
94
  "^#{group_regex}(#|::)"
83
95
  elsif examples.length == 1
@@ -100,7 +112,10 @@ module RubyLsp
100
112
  def handle_test_unit_groups(file_path, groups_and_examples)
101
113
  groups_and_examples.map do |group, info|
102
114
  examples = info[:examples]
103
- group_regex = Shellwords.escape(group).gsub(Shellwords.escape(DYNAMIC_REFERENCE_MARKER), ".*")
115
+ group_regex = Shellwords.escape(group).gsub(
116
+ Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
117
+ ".*",
118
+ )
104
119
  command = +"#{BASE_COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}$/\""
105
120
 
106
121
  unless examples.empty?
@@ -118,33 +133,26 @@ module RubyLsp
118
133
 
119
134
  include Requests::Support::Common
120
135
 
136
+ MINITEST_REPORTER_PATH = File.expand_path("../ruby_lsp_reporter_plugin.rb", __dir__) #: String
137
+ TEST_UNIT_REPORTER_PATH = File.expand_path("../test_unit_test_runner.rb", __dir__) #: String
121
138
  ACCESS_MODIFIERS = [:public, :private, :protected].freeze
122
- DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>"
123
- BASE_COMMAND = T.let(
124
- begin
125
- Bundler.with_original_env { Bundler.default_lockfile }
126
- "bundle exec ruby"
127
- rescue Bundler::GemfileNotFound
128
- "ruby"
129
- end,
130
- String,
131
- )
139
+ BASE_COMMAND = begin
140
+ Bundler.with_original_env { Bundler.default_lockfile }
141
+ "bundle exec ruby"
142
+ rescue Bundler::GemfileNotFound
143
+ "ruby"
144
+ end #: String
132
145
 
133
146
  #: (ResponseBuilders::TestCollection response_builder, GlobalState global_state, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
134
147
  def initialize(response_builder, global_state, dispatcher, uri)
135
- @response_builder = response_builder
136
- @uri = uri
137
- @index = T.let(global_state.index, RubyIndexer::Index)
138
- @visibility_stack = T.let([:public], T::Array[Symbol])
139
- @nesting = T.let([], T::Array[String])
140
- @framework_tag = T.let(:minitest, Symbol)
148
+ super
149
+
150
+ @framework = :minitest #: Symbol
141
151
 
142
152
  dispatcher.register(
143
153
  self,
154
+ # Common handlers registered in parent class
144
155
  :on_class_node_enter,
145
- :on_class_node_leave,
146
- :on_module_node_enter,
147
- :on_module_node_leave,
148
156
  :on_def_node_enter,
149
157
  :on_call_node_enter,
150
158
  :on_call_node_leave,
@@ -153,55 +161,19 @@ module RubyLsp
153
161
 
154
162
  #: (Prism::ClassNode node) -> void
155
163
  def on_class_node_enter(node)
156
- @visibility_stack << :public
157
- name = constant_name(node.constant_path)
158
- name ||= name_with_dynamic_reference(node.constant_path)
159
-
160
- fully_qualified_name = RubyIndexer::Index.actual_nesting(@nesting, name).join("::")
161
-
162
- attached_ancestors = begin
163
- @index.linearized_ancestors_of(fully_qualified_name)
164
- rescue RubyIndexer::Index::NonExistingNamespaceError
165
- # When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
166
- # provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
167
- [node.superclass&.slice].compact
168
- end
169
-
170
- @framework_tag = :test_unit if attached_ancestors.include?("Test::Unit::TestCase")
171
-
172
- if @framework_tag == :test_unit || non_declarative_minitest?(attached_ancestors, fully_qualified_name)
173
- @response_builder.add(Requests::Support::TestItem.new(
174
- fully_qualified_name,
175
- fully_qualified_name,
176
- @uri,
177
- range_from_node(node),
178
- tags: [@framework_tag],
179
- ))
164
+ with_test_ancestor_tracking(node) do |name, ancestors|
165
+ @framework = :test_unit if ancestors.include?("Test::Unit::TestCase")
166
+
167
+ if @framework == :test_unit || non_declarative_minitest?(ancestors, name)
168
+ @response_builder.add(Requests::Support::TestItem.new(
169
+ name,
170
+ name,
171
+ @uri,
172
+ range_from_node(node),
173
+ framework: @framework,
174
+ ))
175
+ end
180
176
  end
181
-
182
- @nesting << name
183
- end
184
-
185
- #: (Prism::ModuleNode node) -> void
186
- def on_module_node_enter(node)
187
- @visibility_stack << :public
188
-
189
- name = constant_name(node.constant_path)
190
- name ||= name_with_dynamic_reference(node.constant_path)
191
-
192
- @nesting << name
193
- end
194
-
195
- #: (Prism::ModuleNode node) -> void
196
- def on_module_node_leave(node)
197
- @visibility_stack.pop
198
- @nesting.pop
199
- end
200
-
201
- #: (Prism::ClassNode node) -> void
202
- def on_class_node_leave(node)
203
- @visibility_stack.pop
204
- @nesting.pop
205
177
  end
206
178
 
207
179
  #: (Prism::DefNode node) -> void
@@ -224,7 +196,7 @@ module RubyLsp
224
196
  name,
225
197
  @uri,
226
198
  range_from_node(node),
227
- tags: [@framework_tag],
199
+ framework: @framework,
228
200
  ))
229
201
  end
230
202
 
@@ -259,12 +231,6 @@ module RubyLsp
259
231
  rescue RubyIndexer::Index::NonExistingNamespaceError
260
232
  true
261
233
  end
262
-
263
- #: ((Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) node) -> String
264
- def name_with_dynamic_reference(node)
265
- slice = node.slice
266
- slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
267
- end
268
234
  end
269
235
  end
270
236
  end
@@ -25,13 +25,13 @@ module RubyLsp
25
25
  @call_node = call_node
26
26
 
27
27
  nesting, surrounding_method = handle_nesting_nodes(nesting_nodes)
28
- @nesting = T.let(nesting, T::Array[String])
29
- @surrounding_method = T.let(surrounding_method, T.nilable(String))
28
+ @nesting = nesting #: Array[String]
29
+ @surrounding_method = surrounding_method #: String?
30
30
  end
31
31
 
32
32
  #: -> String
33
33
  def fully_qualified_name
34
- @fully_qualified_name ||= T.let(@nesting.join("::"), T.nilable(String))
34
+ @fully_qualified_name ||= @nesting.join("::") #: String?
35
35
  end
36
36
 
37
37
  #: -> Array[Symbol]
@@ -55,7 +55,7 @@ module RubyLsp
55
55
  #: (Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)] nodes) -> [Array[String], String?]
56
56
  def handle_nesting_nodes(nodes)
57
57
  nesting = []
58
- surrounding_method = T.let(nil, T.nilable(String))
58
+ surrounding_method = nil #: String?
59
59
 
60
60
  @nesting_nodes.each do |node|
61
61
  case node
@@ -9,7 +9,7 @@ module RubyLsp
9
9
 
10
10
  #: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
11
11
  def initialize(source:, version:, uri:, global_state:)
12
- @syntax_error = T.let(false, T::Boolean)
12
+ @syntax_error = false #: bool
13
13
  super
14
14
  end
15
15