ruby-lsp 0.23.10 → 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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp-launcher +12 -11
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +81 -115
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +117 -166
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -201
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +63 -192
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +14 -16
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +22 -45
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +42 -60
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +75 -0
- data/lib/ruby_indexer/test/configuration_test.rb +42 -3
- data/lib/ruby_indexer/test/index_test.rb +21 -0
- data/lib/ruby_indexer/test/method_test.rb +28 -2
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +44 -71
- data/lib/ruby_lsp/base_server.rb +31 -33
- data/lib/ruby_lsp/client_capabilities.rb +10 -12
- data/lib/ruby_lsp/document.rb +34 -45
- data/lib/ruby_lsp/erb_document.rb +24 -36
- data/lib/ruby_lsp/global_state.rb +51 -56
- data/lib/ruby_lsp/internal.rb +6 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +81 -88
- data/lib/ruby_lsp/listeners/completion.rb +36 -55
- data/lib/ruby_lsp/listeners/definition.rb +37 -51
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +43 -62
- data/lib/ruby_lsp/listeners/document_symbol.rb +35 -49
- data/lib/ruby_lsp/listeners/folding_ranges.rb +32 -39
- data/lib/ruby_lsp/listeners/hover.rb +81 -100
- data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +42 -51
- data/lib/ruby_lsp/listeners/signature_help.rb +6 -25
- data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
- data/lib/ruby_lsp/listeners/test_style.rb +236 -0
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +8 -6
- data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +6 -17
- data/lib/ruby_lsp/requests/completion.rb +7 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +5 -5
- data/lib/ruby_lsp/requests/definition.rb +8 -17
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +75 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
- data/lib/ruby_lsp/requests/hover.rb +8 -18
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
- data/lib/ruby_lsp/requests/on_type_formatting.rb +28 -38
- data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +6 -36
- data/lib/ruby_lsp/requests/rename.rb +11 -37
- data/lib/ruby_lsp/requests/request.rb +7 -19
- data/lib/ruby_lsp/requests/selection_ranges.rb +5 -5
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +12 -31
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +5 -6
- data/lib/ruby_lsp/requests/signature_help.rb +8 -26
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +13 -48
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +9 -12
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +16 -30
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +55 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
- data/lib/ruby_lsp/response_builders/document_symbol.rb +10 -16
- data/lib/ruby_lsp/response_builders/hover.rb +10 -13
- data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +59 -87
- data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
- data/lib/ruby_lsp/response_builders/test_collection.rb +34 -0
- data/lib/ruby_lsp/ruby_document.rb +22 -60
- data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +109 -0
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/server.rb +177 -72
- data/lib/ruby_lsp/setup_bundler.rb +61 -59
- data/lib/ruby_lsp/static_docs.rb +4 -7
- data/lib/ruby_lsp/store.rb +21 -40
- data/lib/ruby_lsp/test_helper.rb +3 -12
- data/lib/ruby_lsp/test_reporter.rb +207 -0
- data/lib/ruby_lsp/test_unit_test_runner.rb +98 -0
- data/lib/ruby_lsp/type_inferrer.rb +9 -13
- data/lib/ruby_lsp/utils.rb +37 -81
- metadata +13 -3
@@ -5,31 +5,22 @@ module RubyLsp
|
|
5
5
|
module Listeners
|
6
6
|
class SemanticHighlighting
|
7
7
|
include Requests::Support::Common
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
T::Array[String],
|
19
|
-
)
|
20
|
-
|
21
|
-
sig do
|
22
|
-
params(
|
23
|
-
dispatcher: Prism::Dispatcher,
|
24
|
-
response_builder: ResponseBuilders::SemanticHighlighting,
|
25
|
-
).void
|
26
|
-
end
|
8
|
+
|
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]
|
16
|
+
|
17
|
+
#: (Prism::Dispatcher dispatcher, ResponseBuilders::SemanticHighlighting response_builder) -> void
|
27
18
|
def initialize(dispatcher, response_builder)
|
28
19
|
@response_builder = response_builder
|
29
|
-
@special_methods =
|
30
|
-
@current_scope =
|
31
|
-
@inside_regex_capture =
|
32
|
-
@inside_implicit_node =
|
20
|
+
@special_methods = nil #: Array[String]?
|
21
|
+
@current_scope = Scope.new #: Scope
|
22
|
+
@inside_regex_capture = false #: bool
|
23
|
+
@inside_implicit_node = false #: bool
|
33
24
|
|
34
25
|
dispatcher.register(
|
35
26
|
self,
|
@@ -62,7 +53,7 @@ module RubyLsp
|
|
62
53
|
)
|
63
54
|
end
|
64
55
|
|
65
|
-
|
56
|
+
#: (Prism::CallNode node) -> void
|
66
57
|
def on_call_node_enter(node)
|
67
58
|
return if @inside_implicit_node
|
68
59
|
|
@@ -85,7 +76,7 @@ module RubyLsp
|
|
85
76
|
end
|
86
77
|
end
|
87
78
|
|
88
|
-
|
79
|
+
#: (Prism::MatchWriteNode node) -> void
|
89
80
|
def on_match_write_node_enter(node)
|
90
81
|
call = node.call
|
91
82
|
|
@@ -95,86 +86,86 @@ module RubyLsp
|
|
95
86
|
end
|
96
87
|
end
|
97
88
|
|
98
|
-
|
89
|
+
#: (Prism::MatchWriteNode node) -> void
|
99
90
|
def on_match_write_node_leave(node)
|
100
91
|
@inside_regex_capture = true if node.call.message == "=~"
|
101
92
|
end
|
102
93
|
|
103
|
-
|
94
|
+
#: (Prism::DefNode node) -> void
|
104
95
|
def on_def_node_enter(node)
|
105
96
|
@current_scope = Scope.new(@current_scope)
|
106
97
|
end
|
107
98
|
|
108
|
-
|
99
|
+
#: (Prism::DefNode node) -> void
|
109
100
|
def on_def_node_leave(node)
|
110
101
|
@current_scope = T.must(@current_scope.parent)
|
111
102
|
end
|
112
103
|
|
113
|
-
|
104
|
+
#: (Prism::BlockNode node) -> void
|
114
105
|
def on_block_node_enter(node)
|
115
106
|
@current_scope = Scope.new(@current_scope)
|
116
107
|
end
|
117
108
|
|
118
|
-
|
109
|
+
#: (Prism::BlockNode node) -> void
|
119
110
|
def on_block_node_leave(node)
|
120
111
|
@current_scope = T.must(@current_scope.parent)
|
121
112
|
end
|
122
113
|
|
123
|
-
|
114
|
+
#: (Prism::BlockLocalVariableNode node) -> void
|
124
115
|
def on_block_local_variable_node_enter(node)
|
125
116
|
@response_builder.add_token(node.location, :variable)
|
126
117
|
end
|
127
118
|
|
128
|
-
|
119
|
+
#: (Prism::BlockParameterNode node) -> void
|
129
120
|
def on_block_parameter_node_enter(node)
|
130
121
|
name = node.name
|
131
122
|
@current_scope.add(name.to_sym, :parameter) if name
|
132
123
|
end
|
133
124
|
|
134
|
-
|
125
|
+
#: (Prism::RequiredKeywordParameterNode node) -> void
|
135
126
|
def on_required_keyword_parameter_node_enter(node)
|
136
127
|
@current_scope.add(node.name, :parameter)
|
137
128
|
end
|
138
129
|
|
139
|
-
|
130
|
+
#: (Prism::OptionalKeywordParameterNode node) -> void
|
140
131
|
def on_optional_keyword_parameter_node_enter(node)
|
141
132
|
@current_scope.add(node.name, :parameter)
|
142
133
|
end
|
143
134
|
|
144
|
-
|
135
|
+
#: (Prism::KeywordRestParameterNode node) -> void
|
145
136
|
def on_keyword_rest_parameter_node_enter(node)
|
146
137
|
name = node.name
|
147
138
|
@current_scope.add(name.to_sym, :parameter) if name
|
148
139
|
end
|
149
140
|
|
150
|
-
|
141
|
+
#: (Prism::OptionalParameterNode node) -> void
|
151
142
|
def on_optional_parameter_node_enter(node)
|
152
143
|
@current_scope.add(node.name, :parameter)
|
153
144
|
end
|
154
145
|
|
155
|
-
|
146
|
+
#: (Prism::RequiredParameterNode node) -> void
|
156
147
|
def on_required_parameter_node_enter(node)
|
157
148
|
@current_scope.add(node.name, :parameter)
|
158
149
|
end
|
159
150
|
|
160
|
-
|
151
|
+
#: (Prism::RestParameterNode node) -> void
|
161
152
|
def on_rest_parameter_node_enter(node)
|
162
153
|
name = node.name
|
163
154
|
@current_scope.add(name.to_sym, :parameter) if name
|
164
155
|
end
|
165
156
|
|
166
|
-
|
157
|
+
#: (Prism::SelfNode node) -> void
|
167
158
|
def on_self_node_enter(node)
|
168
159
|
@response_builder.add_token(node.location, :variable, [:default_library])
|
169
160
|
end
|
170
161
|
|
171
|
-
|
162
|
+
#: (Prism::LocalVariableWriteNode node) -> void
|
172
163
|
def on_local_variable_write_node_enter(node)
|
173
164
|
local = @current_scope.lookup(node.name)
|
174
165
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
175
166
|
end
|
176
167
|
|
177
|
-
|
168
|
+
#: (Prism::LocalVariableReadNode node) -> void
|
178
169
|
def on_local_variable_read_node_enter(node)
|
179
170
|
return if @inside_implicit_node
|
180
171
|
|
@@ -188,25 +179,25 @@ module RubyLsp
|
|
188
179
|
@response_builder.add_token(node.location, local&.type || :variable)
|
189
180
|
end
|
190
181
|
|
191
|
-
|
182
|
+
#: (Prism::LocalVariableAndWriteNode node) -> void
|
192
183
|
def on_local_variable_and_write_node_enter(node)
|
193
184
|
local = @current_scope.lookup(node.name)
|
194
185
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
195
186
|
end
|
196
187
|
|
197
|
-
|
188
|
+
#: (Prism::LocalVariableOperatorWriteNode node) -> void
|
198
189
|
def on_local_variable_operator_write_node_enter(node)
|
199
190
|
local = @current_scope.lookup(node.name)
|
200
191
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
201
192
|
end
|
202
193
|
|
203
|
-
|
194
|
+
#: (Prism::LocalVariableOrWriteNode node) -> void
|
204
195
|
def on_local_variable_or_write_node_enter(node)
|
205
196
|
local = @current_scope.lookup(node.name)
|
206
197
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
207
198
|
end
|
208
199
|
|
209
|
-
|
200
|
+
#: (Prism::LocalVariableTargetNode node) -> void
|
210
201
|
def on_local_variable_target_node_enter(node)
|
211
202
|
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
212
203
|
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
@@ -218,7 +209,7 @@ module RubyLsp
|
|
218
209
|
@response_builder.add_token(node.location, local&.type || :variable)
|
219
210
|
end
|
220
211
|
|
221
|
-
|
212
|
+
#: (Prism::ClassNode node) -> void
|
222
213
|
def on_class_node_enter(node)
|
223
214
|
constant_path = node.constant_path
|
224
215
|
|
@@ -257,7 +248,7 @@ module RubyLsp
|
|
257
248
|
end
|
258
249
|
end
|
259
250
|
|
260
|
-
|
251
|
+
#: (Prism::ModuleNode node) -> void
|
261
252
|
def on_module_node_enter(node)
|
262
253
|
constant_path = node.constant_path
|
263
254
|
|
@@ -278,12 +269,12 @@ module RubyLsp
|
|
278
269
|
end
|
279
270
|
end
|
280
271
|
|
281
|
-
|
272
|
+
#: (Prism::ImplicitNode node) -> void
|
282
273
|
def on_implicit_node_enter(node)
|
283
274
|
@inside_implicit_node = true
|
284
275
|
end
|
285
276
|
|
286
|
-
|
277
|
+
#: (Prism::ImplicitNode node) -> void
|
287
278
|
def on_implicit_node_leave(node)
|
288
279
|
@inside_implicit_node = false
|
289
280
|
end
|
@@ -292,12 +283,12 @@ module RubyLsp
|
|
292
283
|
|
293
284
|
# Textmate provides highlighting for a subset of these special Ruby-specific methods. We want to utilize that
|
294
285
|
# highlighting, so we avoid making a semantic token for it.
|
295
|
-
|
286
|
+
#: (String method_name) -> bool
|
296
287
|
def special_method?(method_name)
|
297
288
|
SPECIAL_RUBY_METHODS.include?(method_name)
|
298
289
|
end
|
299
290
|
|
300
|
-
|
291
|
+
#: (Prism::CallNode node) -> void
|
301
292
|
def process_regexp_locals(node)
|
302
293
|
receiver = node.receiver
|
303
294
|
|
@@ -4,29 +4,20 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
module Listeners
|
6
6
|
class SignatureHelp
|
7
|
-
extend T::Sig
|
8
7
|
include Requests::Support::Common
|
9
8
|
|
10
|
-
|
11
|
-
params(
|
12
|
-
response_builder: ResponseBuilders::SignatureHelp,
|
13
|
-
global_state: GlobalState,
|
14
|
-
node_context: NodeContext,
|
15
|
-
dispatcher: Prism::Dispatcher,
|
16
|
-
sorbet_level: RubyDocument::SorbetLevel,
|
17
|
-
).void
|
18
|
-
end
|
9
|
+
#: (ResponseBuilders::SignatureHelp response_builder, GlobalState global_state, NodeContext node_context, Prism::Dispatcher dispatcher, RubyDocument::SorbetLevel sorbet_level) -> void
|
19
10
|
def initialize(response_builder, global_state, node_context, dispatcher, sorbet_level)
|
20
11
|
@sorbet_level = sorbet_level
|
21
12
|
@response_builder = response_builder
|
22
13
|
@global_state = global_state
|
23
|
-
@index =
|
24
|
-
@type_inferrer =
|
14
|
+
@index = global_state.index #: RubyIndexer::Index
|
15
|
+
@type_inferrer = global_state.type_inferrer #: TypeInferrer
|
25
16
|
@node_context = node_context
|
26
17
|
dispatcher.register(self, :on_call_node_enter)
|
27
18
|
end
|
28
19
|
|
29
|
-
|
20
|
+
#: (Prism::CallNode node) -> void
|
30
21
|
def on_call_node_enter(node)
|
31
22
|
return if sorbet_level_true_or_higher?(@sorbet_level)
|
32
23
|
|
@@ -67,9 +58,7 @@ module RubyLsp
|
|
67
58
|
|
68
59
|
private
|
69
60
|
|
70
|
-
|
71
|
-
params(node: Prism::CallNode, signatures: T::Array[RubyIndexer::Entry::Signature]).returns([Integer, Integer])
|
72
|
-
end
|
61
|
+
#: (Prism::CallNode node, Array[RubyIndexer::Entry::Signature] signatures) -> [Integer, Integer]
|
73
62
|
def determine_active_signature_and_parameter(node, signatures)
|
74
63
|
arguments_node = node.arguments
|
75
64
|
arguments = arguments_node&.arguments || []
|
@@ -93,15 +82,7 @@ module RubyLsp
|
|
93
82
|
[active_sig_index, active_parameter]
|
94
83
|
end
|
95
84
|
|
96
|
-
|
97
|
-
params(
|
98
|
-
signatures: T::Array[RubyIndexer::Entry::Signature],
|
99
|
-
method_name: String,
|
100
|
-
methods: T::Array[RubyIndexer::Entry],
|
101
|
-
title: String,
|
102
|
-
extra_links: T.nilable(String),
|
103
|
-
).returns(T::Array[Interface::SignatureInformation])
|
104
|
-
end
|
85
|
+
#: (Array[RubyIndexer::Entry::Signature] signatures, String method_name, Array[RubyIndexer::Entry] methods, String title, String? extra_links) -> Array[Interface::SignatureInformation]
|
105
86
|
def generate_signatures(signatures, method_name, methods, title, extra_links)
|
106
87
|
signatures.map do |signature|
|
107
88
|
Interface::SignatureInformation.new(
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class SpecStyle < TestDiscovery
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
#: (response_builder: ResponseBuilders::TestCollection, global_state: GlobalState, dispatcher: Prism::Dispatcher, uri: URI::Generic) -> void
|
10
|
+
def initialize(response_builder, global_state, dispatcher, uri)
|
11
|
+
super
|
12
|
+
|
13
|
+
@describe_block_nesting = [] #: Array[String]
|
14
|
+
@spec_class_stack = [] #: Array[bool]
|
15
|
+
|
16
|
+
dispatcher.register(
|
17
|
+
self,
|
18
|
+
# Common handlers registered in parent class
|
19
|
+
:on_class_node_enter,
|
20
|
+
:on_call_node_enter, # e.g. `describe` or `it`
|
21
|
+
:on_call_node_leave,
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
#: (node: Prism::ClassNode) -> void
|
26
|
+
def on_class_node_enter(node)
|
27
|
+
with_test_ancestor_tracking(node) do |_, ancestors|
|
28
|
+
is_spec = ancestors.include?("Minitest::Spec")
|
29
|
+
@spec_class_stack.push(is_spec)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#: (node: Prism::ClassNode) -> void
|
34
|
+
def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
35
|
+
super
|
36
|
+
|
37
|
+
@spec_class_stack.pop
|
38
|
+
end
|
39
|
+
|
40
|
+
#: (node: Prism::CallNode) -> void
|
41
|
+
def on_call_node_enter(node)
|
42
|
+
case node.name
|
43
|
+
when :describe
|
44
|
+
handle_describe(node)
|
45
|
+
when :it, :specify
|
46
|
+
handle_example(node)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#: (node: Prism::CallNode) -> void
|
51
|
+
def on_call_node_leave(node)
|
52
|
+
return unless node.name == :describe && !node.receiver
|
53
|
+
|
54
|
+
@describe_block_nesting.pop
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
#: (node: Prism::CallNode) -> void
|
60
|
+
def handle_describe(node)
|
61
|
+
return if node.block.nil?
|
62
|
+
|
63
|
+
description = extract_description(node)
|
64
|
+
return unless description
|
65
|
+
|
66
|
+
return unless in_spec_context?
|
67
|
+
|
68
|
+
if @nesting.empty? && @describe_block_nesting.empty?
|
69
|
+
test_item = Requests::Support::TestItem.new(
|
70
|
+
description,
|
71
|
+
description,
|
72
|
+
@uri,
|
73
|
+
range_from_node(node),
|
74
|
+
framework: :minitest,
|
75
|
+
)
|
76
|
+
@response_builder.add(test_item)
|
77
|
+
else
|
78
|
+
add_to_parent_test_group(description, node)
|
79
|
+
end
|
80
|
+
|
81
|
+
@describe_block_nesting << description
|
82
|
+
end
|
83
|
+
|
84
|
+
#: (node: Prism::CallNode) -> void
|
85
|
+
def handle_example(node)
|
86
|
+
return unless in_spec_context?
|
87
|
+
|
88
|
+
return if @describe_block_nesting.empty? && @nesting.empty?
|
89
|
+
|
90
|
+
description = extract_description(node)
|
91
|
+
return unless description
|
92
|
+
|
93
|
+
add_to_parent_test_group(description, node)
|
94
|
+
end
|
95
|
+
|
96
|
+
#: (description: String, node: Prism::CallNode) -> void
|
97
|
+
def add_to_parent_test_group(description, node)
|
98
|
+
parent_test_group = find_parent_test_group
|
99
|
+
return unless parent_test_group
|
100
|
+
|
101
|
+
test_item = Requests::Support::TestItem.new(
|
102
|
+
description,
|
103
|
+
description,
|
104
|
+
@uri,
|
105
|
+
range_from_node(node),
|
106
|
+
framework: :minitest,
|
107
|
+
)
|
108
|
+
parent_test_group.add(test_item)
|
109
|
+
end
|
110
|
+
|
111
|
+
#: -> Requests::Support::TestItem?
|
112
|
+
def find_parent_test_group
|
113
|
+
root_group_name, nested_describe_groups = if @nesting.empty?
|
114
|
+
[@describe_block_nesting.first, @describe_block_nesting[1..]]
|
115
|
+
else
|
116
|
+
[RubyIndexer::Index.actual_nesting(@nesting, nil).join("::"), @describe_block_nesting]
|
117
|
+
end
|
118
|
+
return unless root_group_name
|
119
|
+
|
120
|
+
test_group = @response_builder[root_group_name] #: Requests::Support::TestItem?
|
121
|
+
return unless test_group
|
122
|
+
|
123
|
+
return test_group unless nested_describe_groups
|
124
|
+
|
125
|
+
nested_describe_groups.each do |description|
|
126
|
+
test_group = test_group[description]
|
127
|
+
end
|
128
|
+
|
129
|
+
test_group
|
130
|
+
end
|
131
|
+
|
132
|
+
#: (node: Prism::CallNode) -> String?
|
133
|
+
def extract_description(node)
|
134
|
+
first_argument = node.arguments&.arguments&.first
|
135
|
+
return unless first_argument
|
136
|
+
|
137
|
+
case first_argument
|
138
|
+
when Prism::StringNode
|
139
|
+
first_argument.content
|
140
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
141
|
+
constant_name(first_argument)
|
142
|
+
else
|
143
|
+
first_argument.slice
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
#: -> bool
|
148
|
+
def in_spec_context?
|
149
|
+
return true if @nesting.empty?
|
150
|
+
|
151
|
+
T.must(@spec_class_stack.last)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
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
|