ruby-lsp 0.23.10 → 0.23.12

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +12 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +52 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +61 -144
  8. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +8 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -183
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +55 -181
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  12. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +12 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +21 -44
  14. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +40 -58
  15. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
  16. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  17. data/lib/ruby_indexer/test/classes_and_modules_test.rb +75 -0
  18. data/lib/ruby_indexer/test/configuration_test.rb +32 -2
  19. data/lib/ruby_indexer/test/index_test.rb +21 -0
  20. data/lib/ruby_indexer/test/method_test.rb +2 -2
  21. data/lib/ruby_lsp/addon.rb +32 -67
  22. data/lib/ruby_lsp/base_server.rb +12 -11
  23. data/lib/ruby_lsp/client_capabilities.rb +4 -6
  24. data/lib/ruby_lsp/document.rb +21 -32
  25. data/lib/ruby_lsp/erb_document.rb +17 -27
  26. data/lib/ruby_lsp/global_state.rb +30 -32
  27. data/lib/ruby_lsp/internal.rb +6 -0
  28. data/lib/ruby_lsp/listeners/code_lens.rb +21 -39
  29. data/lib/ruby_lsp/listeners/completion.rb +34 -53
  30. data/lib/ruby_lsp/listeners/definition.rb +35 -49
  31. data/lib/ruby_lsp/listeners/document_highlight.rb +60 -69
  32. data/lib/ruby_lsp/listeners/document_link.rb +9 -19
  33. data/lib/ruby_lsp/listeners/document_symbol.rb +34 -48
  34. data/lib/ruby_lsp/listeners/folding_ranges.rb +31 -38
  35. data/lib/ruby_lsp/listeners/hover.rb +37 -47
  36. data/lib/ruby_lsp/listeners/inlay_hints.rb +3 -10
  37. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +29 -35
  38. data/lib/ruby_lsp/listeners/signature_help.rb +4 -23
  39. data/lib/ruby_lsp/listeners/spec_style.rb +199 -0
  40. data/lib/ruby_lsp/listeners/test_style.rb +270 -0
  41. data/lib/ruby_lsp/node_context.rb +8 -35
  42. data/lib/ruby_lsp/rbs_document.rb +7 -5
  43. data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
  44. data/lib/ruby_lsp/requests/code_actions.rb +5 -14
  45. data/lib/ruby_lsp/requests/code_lens.rb +4 -13
  46. data/lib/ruby_lsp/requests/completion.rb +4 -15
  47. data/lib/ruby_lsp/requests/completion_resolve.rb +4 -4
  48. data/lib/ruby_lsp/requests/definition.rb +4 -12
  49. data/lib/ruby_lsp/requests/diagnostics.rb +6 -9
  50. data/lib/ruby_lsp/requests/discover_tests.rb +74 -0
  51. data/lib/ruby_lsp/requests/document_highlight.rb +3 -11
  52. data/lib/ruby_lsp/requests/document_link.rb +4 -13
  53. data/lib/ruby_lsp/requests/document_symbol.rb +4 -7
  54. data/lib/ruby_lsp/requests/folding_ranges.rb +4 -7
  55. data/lib/ruby_lsp/requests/formatting.rb +4 -7
  56. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  57. data/lib/ruby_lsp/requests/hover.rb +6 -16
  58. data/lib/ruby_lsp/requests/inlay_hints.rb +4 -13
  59. data/lib/ruby_lsp/requests/on_type_formatting.rb +17 -24
  60. data/lib/ruby_lsp/requests/prepare_rename.rb +3 -8
  61. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
  62. data/lib/ruby_lsp/requests/range_formatting.rb +3 -4
  63. data/lib/ruby_lsp/requests/references.rb +5 -35
  64. data/lib/ruby_lsp/requests/rename.rb +9 -35
  65. data/lib/ruby_lsp/requests/request.rb +5 -17
  66. data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
  67. data/lib/ruby_lsp/requests/semantic_highlighting.rb +6 -23
  68. data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -5
  69. data/lib/ruby_lsp/requests/signature_help.rb +6 -24
  70. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  71. data/lib/ruby_lsp/requests/support/common.rb +12 -47
  72. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -14
  73. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +7 -10
  74. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -15
  75. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  76. data/lib/ruby_lsp/requests/support/sorbet.rb +1 -7
  77. data/lib/ruby_lsp/requests/support/source_uri.rb +5 -16
  78. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +7 -10
  79. data/lib/ruby_lsp/requests/support/test_item.rb +60 -0
  80. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +4 -5
  81. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -3
  82. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +4 -4
  83. data/lib/ruby_lsp/response_builders/document_symbol.rb +8 -11
  84. data/lib/ruby_lsp/response_builders/hover.rb +5 -5
  85. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  86. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +18 -40
  87. data/lib/ruby_lsp/response_builders/signature_help.rb +4 -5
  88. data/lib/ruby_lsp/response_builders/test_collection.rb +34 -0
  89. data/lib/ruby_lsp/ruby_document.rb +15 -40
  90. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +106 -0
  91. data/lib/ruby_lsp/scope.rb +6 -10
  92. data/lib/ruby_lsp/server.rb +169 -72
  93. data/lib/ruby_lsp/setup_bundler.rb +25 -17
  94. data/lib/ruby_lsp/store.rb +12 -28
  95. data/lib/ruby_lsp/test_helper.rb +3 -12
  96. data/lib/ruby_lsp/test_reporter.rb +71 -0
  97. data/lib/ruby_lsp/test_unit_test_runner.rb +96 -0
  98. data/lib/ruby_lsp/type_inferrer.rb +9 -13
  99. data/lib/ruby_lsp/utils.rb +27 -65
  100. metadata +12 -3
@@ -0,0 +1,199 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Listeners
6
+ class SpecStyle
7
+ extend T::Sig
8
+ include Requests::Support::Common
9
+
10
+ DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>"
11
+
12
+ #: (response_builder: ResponseBuilders::TestCollection, global_state: GlobalState, dispatcher: Prism::Dispatcher, uri: URI::Generic) -> void
13
+ 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])
21
+
22
+ dispatcher.register(
23
+ self,
24
+ :on_class_node_enter,
25
+ :on_class_node_leave,
26
+ :on_module_node_enter,
27
+ :on_module_node_leave,
28
+ :on_call_node_enter, # e.g. `describe` or `it`
29
+ :on_call_node_leave,
30
+ )
31
+ end
32
+
33
+ #: (node: Prism::ClassNode) -> void
34
+ 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
47
+ 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
+ end
70
+
71
+ #: (node: Prism::ClassNode) -> void
72
+ def on_class_node_leave(node)
73
+ @visibility_stack.pop
74
+ @nesting.pop
75
+ @spec_class_stack.pop
76
+ end
77
+
78
+ #: (node: Prism::CallNode) -> void
79
+ def on_call_node_enter(node)
80
+ case node.name
81
+ when :describe
82
+ handle_describe(node)
83
+ when :it, :specify
84
+ handle_example(node)
85
+ end
86
+ end
87
+
88
+ #: (node: Prism::CallNode) -> void
89
+ def on_call_node_leave(node)
90
+ return unless node.name == :describe && !node.receiver
91
+
92
+ @describe_block_nesting.pop
93
+ end
94
+
95
+ private
96
+
97
+ #: (node: Prism::CallNode) -> void
98
+ def handle_describe(node)
99
+ return if node.block.nil?
100
+
101
+ description = extract_description(node)
102
+ return unless description
103
+
104
+ return unless in_spec_context?
105
+
106
+ if @nesting.empty? && @describe_block_nesting.empty?
107
+ test_item = Requests::Support::TestItem.new(
108
+ description,
109
+ description,
110
+ @uri,
111
+ range_from_node(node),
112
+ tags: [:minitest],
113
+ )
114
+ @response_builder.add(test_item)
115
+ else
116
+ add_to_parent_test_group(description, node)
117
+ end
118
+
119
+ @describe_block_nesting << description
120
+ end
121
+
122
+ #: (node: Prism::CallNode) -> void
123
+ def handle_example(node)
124
+ return unless in_spec_context?
125
+
126
+ return if @describe_block_nesting.empty? && @nesting.empty?
127
+
128
+ description = extract_description(node)
129
+ return unless description
130
+
131
+ add_to_parent_test_group(description, node)
132
+ end
133
+
134
+ #: (description: String, node: Prism::CallNode) -> void
135
+ def add_to_parent_test_group(description, node)
136
+ parent_test_group = find_parent_test_group
137
+ return unless parent_test_group
138
+
139
+ test_item = Requests::Support::TestItem.new(
140
+ description,
141
+ description,
142
+ @uri,
143
+ range_from_node(node),
144
+ tags: [:minitest],
145
+ )
146
+ parent_test_group.add(test_item)
147
+ end
148
+
149
+ #: -> Requests::Support::TestItem?
150
+ def find_parent_test_group
151
+ root_group_name, nested_describe_groups = if @nesting.empty?
152
+ [@describe_block_nesting.first, @describe_block_nesting[1..]]
153
+ else
154
+ [RubyIndexer::Index.actual_nesting(@nesting, nil).join("::"), @describe_block_nesting]
155
+ end
156
+ return unless root_group_name
157
+
158
+ test_group = T.let(@response_builder[root_group_name], T.nilable(Requests::Support::TestItem))
159
+ return unless test_group
160
+
161
+ return test_group unless nested_describe_groups
162
+
163
+ nested_describe_groups.each do |description|
164
+ test_group = test_group[description]
165
+ end
166
+
167
+ test_group
168
+ end
169
+
170
+ #: (node: Prism::CallNode) -> String?
171
+ def extract_description(node)
172
+ first_argument = node.arguments&.arguments&.first
173
+ return unless first_argument
174
+
175
+ case first_argument
176
+ when Prism::StringNode
177
+ first_argument.content
178
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
179
+ constant_name(first_argument)
180
+ else
181
+ first_argument.slice
182
+ end
183
+ end
184
+
185
+ #: -> bool
186
+ def in_spec_context?
187
+ return true if @nesting.empty?
188
+
189
+ T.must(@spec_class_stack.last)
190
+ 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
+ end
198
+ end
199
+ end
@@ -0,0 +1,270 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Listeners
6
+ class TestStyle
7
+ class << self
8
+ # Resolves the minimal set of commands required to execute the requested tests
9
+ #: (Array[Hash[Symbol, untyped]]) -> Array[String]
10
+ def resolve_test_commands(items)
11
+ # A nested hash of file_path => test_group => { tags: [], examples: [test_example] } to ensure we build the
12
+ # minimum amount of commands needed to execute the requested tests. This is only used for specific examples
13
+ # where we will need more complex regexes to execute it all at the same time
14
+ aggregated_tests = Hash.new do |hash, key|
15
+ hash[key] = Hash.new do |inner_h, inner_k|
16
+ inner_h[inner_k] = { tags: Set.new, examples: [] }
17
+ end
18
+ end
19
+
20
+ # Full files are paths that should be executed as a whole e.g.: an entire test file or directory
21
+ full_files = []
22
+ queue = items.dup
23
+
24
+ until queue.empty?
25
+ item = T.must(queue.shift)
26
+ tags = Set.new(item[:tags])
27
+
28
+ children = item[:children]
29
+ uri = URI(item[:uri])
30
+ path = uri.full_path
31
+ next unless path
32
+
33
+ if tags.include?("test_dir")
34
+ full_files << "#{path}/**/*" if children.empty?
35
+ elsif tags.include?("test_file")
36
+ full_files << path if children.empty?
37
+ elsif tags.include?("test_group")
38
+ # If all of the children of the current test group are other groups, then there's no need to add it to the
39
+ # aggregated examples
40
+ unless children.any? && children.all? { |child| child[:tags].include?("test_group") }
41
+ aggregated_tests[path][item[:label]] = { tags: tags, examples: [] }
42
+ end
43
+ elsif tags.include?("minitest") || tags.include?("test_unit")
44
+ class_name, method_name = item[:id].split("#")
45
+ aggregated_tests[path][class_name][:examples] << method_name
46
+ aggregated_tests[path][class_name][:tags].merge(tags)
47
+ end
48
+
49
+ queue.concat(children) unless children.empty?
50
+ end
51
+
52
+ commands = []
53
+
54
+ aggregated_tests.each do |file_path, groups_and_examples|
55
+ # Separate groups into Minitest and Test Unit. You can have both frameworks in the same file, but you cannot
56
+ # have a group belongs to both at the same time
57
+ minitest_groups, test_unit_groups = groups_and_examples.partition do |_, info|
58
+ info[:tags].include?("minitest")
59
+ end
60
+
61
+ if minitest_groups.any?
62
+ commands << handle_minitest_groups(file_path, minitest_groups)
63
+ end
64
+
65
+ if test_unit_groups.any?
66
+ commands.concat(handle_test_unit_groups(file_path, test_unit_groups))
67
+ end
68
+ end
69
+
70
+ commands << "#{BASE_COMMAND} -Itest #{full_files.join(" ")}" unless full_files.empty?
71
+ commands
72
+ end
73
+
74
+ private
75
+
76
+ #: (String, Hash[String, Hash[Symbol, untyped]]) -> String
77
+ def handle_minitest_groups(file_path, groups_and_examples)
78
+ regexes = groups_and_examples.flat_map do |group, info|
79
+ examples = info[:examples]
80
+ group_regex = Shellwords.escape(group).gsub(Shellwords.escape(DYNAMIC_REFERENCE_MARKER), ".*")
81
+ if examples.empty?
82
+ "^#{group_regex}(#|::)"
83
+ elsif examples.length == 1
84
+ "^#{group_regex}##{examples[0]}$"
85
+ else
86
+ "^#{group_regex}#(#{examples.join("|")})$"
87
+ end
88
+ end
89
+
90
+ regex = if regexes.length == 1
91
+ regexes[0]
92
+ else
93
+ "(#{regexes.join("|")})"
94
+ end
95
+
96
+ "#{BASE_COMMAND} -Itest #{file_path} --name \"/#{regex}/\""
97
+ end
98
+
99
+ #: (String, Hash[String, Hash[Symbol, untyped]]) -> Array[String]
100
+ def handle_test_unit_groups(file_path, groups_and_examples)
101
+ groups_and_examples.map do |group, info|
102
+ examples = info[:examples]
103
+ group_regex = Shellwords.escape(group).gsub(Shellwords.escape(DYNAMIC_REFERENCE_MARKER), ".*")
104
+ command = +"#{BASE_COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}$/\""
105
+
106
+ unless examples.empty?
107
+ command << if examples.length == 1
108
+ " --name \"/#{examples[0]}$/\""
109
+ else
110
+ " --name \"/(#{examples.join("|")})$/\""
111
+ end
112
+ end
113
+
114
+ command
115
+ end
116
+ end
117
+ end
118
+
119
+ include Requests::Support::Common
120
+
121
+ 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
+ )
132
+
133
+ #: (ResponseBuilders::TestCollection response_builder, GlobalState global_state, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
134
+ 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)
141
+
142
+ dispatcher.register(
143
+ self,
144
+ :on_class_node_enter,
145
+ :on_class_node_leave,
146
+ :on_module_node_enter,
147
+ :on_module_node_leave,
148
+ :on_def_node_enter,
149
+ :on_call_node_enter,
150
+ :on_call_node_leave,
151
+ )
152
+ end
153
+
154
+ #: (Prism::ClassNode node) -> void
155
+ 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
+ ))
180
+ 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
+ end
206
+
207
+ #: (Prism::DefNode node) -> void
208
+ def on_def_node_enter(node)
209
+ return if @visibility_stack.last != :public
210
+
211
+ name = node.name.to_s
212
+ return unless name.start_with?("test_")
213
+
214
+ current_group_name = RubyIndexer::Index.actual_nesting(@nesting, nil).join("::")
215
+
216
+ # If we're finding a test method, but for the wrong framework, then the group test item will not have been
217
+ # previously pushed and thus we return early and avoid adding items for a framework this listener is not
218
+ # interested in
219
+ test_item = @response_builder[current_group_name]
220
+ return unless test_item
221
+
222
+ test_item.add(Requests::Support::TestItem.new(
223
+ "#{current_group_name}##{name}",
224
+ name,
225
+ @uri,
226
+ range_from_node(node),
227
+ tags: [@framework_tag],
228
+ ))
229
+ end
230
+
231
+ #: (Prism::CallNode node) -> void
232
+ def on_call_node_enter(node)
233
+ name = node.name
234
+ return unless ACCESS_MODIFIERS.include?(name)
235
+
236
+ @visibility_stack << name
237
+ end
238
+
239
+ #: (Prism::CallNode node) -> void
240
+ def on_call_node_leave(node)
241
+ name = node.name
242
+ return unless ACCESS_MODIFIERS.include?(name)
243
+ return unless node.arguments&.arguments
244
+
245
+ @visibility_stack.pop
246
+ end
247
+
248
+ private
249
+
250
+ #: (Array[String] attached_ancestors, String fully_qualified_name) -> bool
251
+ def non_declarative_minitest?(attached_ancestors, fully_qualified_name)
252
+ return false unless attached_ancestors.include?("Minitest::Test")
253
+
254
+ # We only support regular Minitest tests. The declarative syntax provided by ActiveSupport is handled by the
255
+ # Rails add-on
256
+ name_parts = fully_qualified_name.split("::")
257
+ singleton_name = "#{name_parts.join("::")}::<Class:#{name_parts.last}>"
258
+ !@index.linearized_ancestors_of(singleton_name).include?("ActiveSupport::Testing::Declarative")
259
+ rescue RubyIndexer::Index::NonExistingNamespaceError
260
+ true
261
+ 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
+ end
269
+ end
270
+ end
@@ -5,36 +5,19 @@ module RubyLsp
5
5
  # This class allows listeners to access contextual information about a node in the AST, such as its parent,
6
6
  # its namespace nesting, and the surrounding CallNode (e.g. a method call).
7
7
  class NodeContext
8
- extend T::Sig
9
-
10
- sig { returns(T.nilable(Prism::Node)) }
8
+ #: Prism::Node?
11
9
  attr_reader :node, :parent
12
10
 
13
- sig { returns(T::Array[String]) }
11
+ #: Array[String]
14
12
  attr_reader :nesting
15
13
 
16
- sig { returns(T.nilable(Prism::CallNode)) }
14
+ #: Prism::CallNode?
17
15
  attr_reader :call_node
18
16
 
19
- sig { returns(T.nilable(String)) }
17
+ #: String?
20
18
  attr_reader :surrounding_method
21
19
 
22
- sig do
23
- params(
24
- node: T.nilable(Prism::Node),
25
- parent: T.nilable(Prism::Node),
26
- nesting_nodes: T::Array[T.any(
27
- Prism::ClassNode,
28
- Prism::ModuleNode,
29
- Prism::SingletonClassNode,
30
- Prism::DefNode,
31
- Prism::BlockNode,
32
- Prism::LambdaNode,
33
- Prism::ProgramNode,
34
- )],
35
- call_node: T.nilable(Prism::CallNode),
36
- ).void
37
- end
20
+ #: (Prism::Node? node, Prism::Node? parent, Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)] nesting_nodes, Prism::CallNode? call_node) -> void
38
21
  def initialize(node, parent, nesting_nodes, call_node)
39
22
  @node = node
40
23
  @parent = parent
@@ -46,12 +29,12 @@ module RubyLsp
46
29
  @surrounding_method = T.let(surrounding_method, T.nilable(String))
47
30
  end
48
31
 
49
- sig { returns(String) }
32
+ #: -> String
50
33
  def fully_qualified_name
51
34
  @fully_qualified_name ||= T.let(@nesting.join("::"), T.nilable(String))
52
35
  end
53
36
 
54
- sig { returns(T::Array[Symbol]) }
37
+ #: -> Array[Symbol]
55
38
  def locals_for_scope
56
39
  locals = []
57
40
 
@@ -69,17 +52,7 @@ module RubyLsp
69
52
 
70
53
  private
71
54
 
72
- sig do
73
- params(nodes: T::Array[T.any(
74
- Prism::ClassNode,
75
- Prism::ModuleNode,
76
- Prism::SingletonClassNode,
77
- Prism::DefNode,
78
- Prism::BlockNode,
79
- Prism::LambdaNode,
80
- Prism::ProgramNode,
81
- )]).returns([T::Array[String], T.nilable(String)])
82
- end
55
+ #: (Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)] nodes) -> [Array[String], String?]
83
56
  def handle_nesting_nodes(nodes)
84
57
  nesting = []
85
58
  surrounding_method = T.let(nil, T.nilable(String))
@@ -3,18 +3,18 @@
3
3
 
4
4
  module RubyLsp
5
5
  class RBSDocument < Document
6
- extend T::Sig
7
6
  extend T::Generic
8
7
 
9
8
  ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
10
9
 
11
- sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
10
+ #: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
12
11
  def initialize(source:, version:, uri:, global_state:)
13
12
  @syntax_error = T.let(false, T::Boolean)
14
13
  super
15
14
  end
16
15
 
17
- sig { override.returns(T::Boolean) }
16
+ # @override
17
+ #: -> bool
18
18
  def parse!
19
19
  return false unless @needs_parsing
20
20
 
@@ -29,12 +29,14 @@ module RubyLsp
29
29
  true
30
30
  end
31
31
 
32
- sig { override.returns(T::Boolean) }
32
+ # @override
33
+ #: -> bool
33
34
  def syntax_error?
34
35
  @syntax_error
35
36
  end
36
37
 
37
- sig { override.returns(LanguageId) }
38
+ # @override
39
+ #: -> LanguageId
38
40
  def language_id
39
41
  LanguageId::RBS
40
42
  end
@@ -7,7 +7,6 @@ module RubyLsp
7
7
  # request is used to to resolve the edit field for a given code action, if it is not already provided in the
8
8
  # textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
9
9
  class CodeActionResolve < Request
10
- extend T::Sig
11
10
  include Support::Common
12
11
 
13
12
  NEW_VARIABLE_NAME = "new_variable"
@@ -23,7 +22,7 @@ module RubyLsp
23
22
  end
24
23
  end
25
24
 
26
- sig { params(document: RubyDocument, global_state: GlobalState, code_action: T::Hash[Symbol, T.untyped]).void }
25
+ #: (RubyDocument document, GlobalState global_state, Hash[Symbol, untyped] code_action) -> void
27
26
  def initialize(document, global_state, code_action)
28
27
  super()
29
28
  @document = document
@@ -31,7 +30,8 @@ module RubyLsp
31
30
  @code_action = code_action
32
31
  end
33
32
 
34
- sig { override.returns(T.any(Interface::CodeAction, Error)) }
33
+ # @override
34
+ #: -> (Interface::CodeAction | Error)
35
35
  def perform
36
36
  return Error::EmptySelection if @document.source.empty?
37
37
 
@@ -53,7 +53,7 @@ module RubyLsp
53
53
 
54
54
  private
55
55
 
56
- sig { returns(T.any(Interface::CodeAction, Error)) }
56
+ #: -> (Interface::CodeAction | Error)
57
57
  def switch_block_style
58
58
  source_range = @code_action.dig(:data, :range)
59
59
  return Error::EmptySelection if source_range[:start] == source_range[:end]
@@ -91,7 +91,7 @@ module RubyLsp
91
91
  )
92
92
  end
93
93
 
94
- sig { returns(T.any(Interface::CodeAction, Error)) }
94
+ #: -> (Interface::CodeAction | Error)
95
95
  def refactor_variable
96
96
  source_range = @code_action.dig(:data, :range)
97
97
  return Error::EmptySelection if source_range[:start] == source_range[:end]
@@ -189,7 +189,7 @@ module RubyLsp
189
189
  )
190
190
  end
191
191
 
192
- sig { returns(T.any(Interface::CodeAction, Error)) }
192
+ #: -> (Interface::CodeAction | Error)
193
193
  def refactor_method
194
194
  source_range = @code_action.dig(:data, :range)
195
195
  return Error::EmptySelection if source_range[:start] == source_range[:end]
@@ -261,7 +261,7 @@ module RubyLsp
261
261
  )
262
262
  end
263
263
 
264
- sig { params(range: T::Hash[Symbol, T.untyped], new_text: String).returns(Interface::TextEdit) }
264
+ #: (Hash[Symbol, untyped] range, String new_text) -> Interface::TextEdit
265
265
  def create_text_edit(range, new_text)
266
266
  Interface::TextEdit.new(
267
267
  range: Interface::Range.new(
@@ -272,7 +272,7 @@ module RubyLsp
272
272
  )
273
273
  end
274
274
 
275
- sig { params(node: Prism::BlockNode, indentation: T.nilable(String)).returns(String) }
275
+ #: (Prism::BlockNode node, String? indentation) -> String
276
276
  def recursively_switch_nested_block_styles(node, indentation)
277
277
  parameters = node.parameters
278
278
  body = node.body
@@ -301,7 +301,7 @@ module RubyLsp
301
301
  source
302
302
  end
303
303
 
304
- sig { params(body: Prism::Node, indentation: T.nilable(String)).returns(String) }
304
+ #: (Prism::Node body, String? indentation) -> String
305
305
  def switch_block_body(body, indentation)
306
306
  # Check if there are any nested blocks inside of the current block
307
307
  body_loc = body.location
@@ -330,7 +330,7 @@ module RubyLsp
330
330
  indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
331
331
  end
332
332
 
333
- sig { returns(T.any(Interface::CodeAction, Error)) }
333
+ #: -> (Interface::CodeAction | Error)
334
334
  def create_attribute_accessor
335
335
  source_range = @code_action.dig(:data, :range)
336
336