ruby-lsp 0.23.11 → 0.23.17
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 +20 -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 +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +134 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -10
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +97 -217
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +139 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +18 -19
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +23 -55
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +47 -61
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +48 -7
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +146 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
- data/lib/ruby_indexer/test/method_test.rb +149 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +68 -73
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +44 -71
- data/lib/ruby_lsp/base_server.rb +29 -32
- data/lib/ruby_lsp/client_capabilities.rb +10 -12
- data/lib/ruby_lsp/document.rb +40 -54
- data/lib/ruby_lsp/erb_document.rb +37 -41
- data/lib/ruby_lsp/global_state.rb +52 -57
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +67 -73
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +50 -70
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +92 -110
- data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- 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 +167 -90
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +9 -7
- data/lib/ruby_lsp/requests/code_action_resolve.rb +63 -59
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +20 -19
- data/lib/ruby_lsp/requests/completion.rb +7 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +7 -17
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
- 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 +85 -0
- data/lib/ruby_lsp/requests/hover.rb +10 -20
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +9 -53
- data/lib/ruby_lsp/requests/rename.rb +20 -46
- data/lib/ruby_lsp/requests/request.rb +8 -19
- data/lib/ruby_lsp/requests/selection_ranges.rb +6 -6
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- 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 +15 -55
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +11 -14
- 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 +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- 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 +14 -19
- data/lib/ruby_lsp/response_builders/hover.rb +11 -14
- data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +60 -88
- data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
- data/lib/ruby_lsp/response_builders/test_collection.rb +43 -10
- data/lib/ruby_lsp/ruby_document.rb +24 -92
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +182 -99
- data/lib/ruby_lsp/setup_bundler.rb +65 -60
- data/lib/ruby_lsp/static_docs.rb +11 -7
- data/lib/ruby_lsp/store.rb +29 -47
- data/lib/ruby_lsp/test_helper.rb +2 -12
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +191 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +92 -83
- metadata +9 -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
|
|
@@ -76,16 +67,22 @@ module RubyLsp
|
|
76
67
|
return if special_method?(message)
|
77
68
|
|
78
69
|
if Requests::Support::Sorbet.annotation?(node)
|
79
|
-
@response_builder.add_token(
|
70
|
+
@response_builder.add_token(
|
71
|
+
node.message_loc, #: as !nil
|
72
|
+
:type,
|
73
|
+
)
|
80
74
|
elsif !node.receiver && !node.opening_loc
|
81
75
|
# If the node has a receiver, then the syntax is not ambiguous and semantic highlighting is not necessary to
|
82
76
|
# determine that the token is a method call. The only ambiguous case is method calls with implicit self
|
83
77
|
# receiver and no parenthesis, which may be confused with local variables
|
84
|
-
@response_builder.add_token(
|
78
|
+
@response_builder.add_token(
|
79
|
+
node.message_loc, #: as !nil
|
80
|
+
:method,
|
81
|
+
)
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
88
|
-
|
85
|
+
#: (Prism::MatchWriteNode node) -> void
|
89
86
|
def on_match_write_node_enter(node)
|
90
87
|
call = node.call
|
91
88
|
|
@@ -95,86 +92,86 @@ module RubyLsp
|
|
95
92
|
end
|
96
93
|
end
|
97
94
|
|
98
|
-
|
95
|
+
#: (Prism::MatchWriteNode node) -> void
|
99
96
|
def on_match_write_node_leave(node)
|
100
97
|
@inside_regex_capture = true if node.call.message == "=~"
|
101
98
|
end
|
102
99
|
|
103
|
-
|
100
|
+
#: (Prism::DefNode node) -> void
|
104
101
|
def on_def_node_enter(node)
|
105
102
|
@current_scope = Scope.new(@current_scope)
|
106
103
|
end
|
107
104
|
|
108
|
-
|
105
|
+
#: (Prism::DefNode node) -> void
|
109
106
|
def on_def_node_leave(node)
|
110
|
-
@current_scope =
|
107
|
+
@current_scope = @current_scope.parent #: as !nil
|
111
108
|
end
|
112
109
|
|
113
|
-
|
110
|
+
#: (Prism::BlockNode node) -> void
|
114
111
|
def on_block_node_enter(node)
|
115
112
|
@current_scope = Scope.new(@current_scope)
|
116
113
|
end
|
117
114
|
|
118
|
-
|
115
|
+
#: (Prism::BlockNode node) -> void
|
119
116
|
def on_block_node_leave(node)
|
120
|
-
@current_scope =
|
117
|
+
@current_scope = @current_scope.parent #: as !nil
|
121
118
|
end
|
122
119
|
|
123
|
-
|
120
|
+
#: (Prism::BlockLocalVariableNode node) -> void
|
124
121
|
def on_block_local_variable_node_enter(node)
|
125
122
|
@response_builder.add_token(node.location, :variable)
|
126
123
|
end
|
127
124
|
|
128
|
-
|
125
|
+
#: (Prism::BlockParameterNode node) -> void
|
129
126
|
def on_block_parameter_node_enter(node)
|
130
127
|
name = node.name
|
131
128
|
@current_scope.add(name.to_sym, :parameter) if name
|
132
129
|
end
|
133
130
|
|
134
|
-
|
131
|
+
#: (Prism::RequiredKeywordParameterNode node) -> void
|
135
132
|
def on_required_keyword_parameter_node_enter(node)
|
136
133
|
@current_scope.add(node.name, :parameter)
|
137
134
|
end
|
138
135
|
|
139
|
-
|
136
|
+
#: (Prism::OptionalKeywordParameterNode node) -> void
|
140
137
|
def on_optional_keyword_parameter_node_enter(node)
|
141
138
|
@current_scope.add(node.name, :parameter)
|
142
139
|
end
|
143
140
|
|
144
|
-
|
141
|
+
#: (Prism::KeywordRestParameterNode node) -> void
|
145
142
|
def on_keyword_rest_parameter_node_enter(node)
|
146
143
|
name = node.name
|
147
144
|
@current_scope.add(name.to_sym, :parameter) if name
|
148
145
|
end
|
149
146
|
|
150
|
-
|
147
|
+
#: (Prism::OptionalParameterNode node) -> void
|
151
148
|
def on_optional_parameter_node_enter(node)
|
152
149
|
@current_scope.add(node.name, :parameter)
|
153
150
|
end
|
154
151
|
|
155
|
-
|
152
|
+
#: (Prism::RequiredParameterNode node) -> void
|
156
153
|
def on_required_parameter_node_enter(node)
|
157
154
|
@current_scope.add(node.name, :parameter)
|
158
155
|
end
|
159
156
|
|
160
|
-
|
157
|
+
#: (Prism::RestParameterNode node) -> void
|
161
158
|
def on_rest_parameter_node_enter(node)
|
162
159
|
name = node.name
|
163
160
|
@current_scope.add(name.to_sym, :parameter) if name
|
164
161
|
end
|
165
162
|
|
166
|
-
|
163
|
+
#: (Prism::SelfNode node) -> void
|
167
164
|
def on_self_node_enter(node)
|
168
165
|
@response_builder.add_token(node.location, :variable, [:default_library])
|
169
166
|
end
|
170
167
|
|
171
|
-
|
168
|
+
#: (Prism::LocalVariableWriteNode node) -> void
|
172
169
|
def on_local_variable_write_node_enter(node)
|
173
170
|
local = @current_scope.lookup(node.name)
|
174
171
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
175
172
|
end
|
176
173
|
|
177
|
-
|
174
|
+
#: (Prism::LocalVariableReadNode node) -> void
|
178
175
|
def on_local_variable_read_node_enter(node)
|
179
176
|
return if @inside_implicit_node
|
180
177
|
|
@@ -188,25 +185,25 @@ module RubyLsp
|
|
188
185
|
@response_builder.add_token(node.location, local&.type || :variable)
|
189
186
|
end
|
190
187
|
|
191
|
-
|
188
|
+
#: (Prism::LocalVariableAndWriteNode node) -> void
|
192
189
|
def on_local_variable_and_write_node_enter(node)
|
193
190
|
local = @current_scope.lookup(node.name)
|
194
191
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
195
192
|
end
|
196
193
|
|
197
|
-
|
194
|
+
#: (Prism::LocalVariableOperatorWriteNode node) -> void
|
198
195
|
def on_local_variable_operator_write_node_enter(node)
|
199
196
|
local = @current_scope.lookup(node.name)
|
200
197
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
201
198
|
end
|
202
199
|
|
203
|
-
|
200
|
+
#: (Prism::LocalVariableOrWriteNode node) -> void
|
204
201
|
def on_local_variable_or_write_node_enter(node)
|
205
202
|
local = @current_scope.lookup(node.name)
|
206
203
|
@response_builder.add_token(node.name_loc, :parameter) if local&.type == :parameter
|
207
204
|
end
|
208
205
|
|
209
|
-
|
206
|
+
#: (Prism::LocalVariableTargetNode node) -> void
|
210
207
|
def on_local_variable_target_node_enter(node)
|
211
208
|
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
212
209
|
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
@@ -218,7 +215,7 @@ module RubyLsp
|
|
218
215
|
@response_builder.add_token(node.location, local&.type || :variable)
|
219
216
|
end
|
220
217
|
|
221
|
-
|
218
|
+
#: (Prism::ClassNode node) -> void
|
222
219
|
def on_class_node_enter(node)
|
223
220
|
constant_path = node.constant_path
|
224
221
|
|
@@ -257,7 +254,7 @@ module RubyLsp
|
|
257
254
|
end
|
258
255
|
end
|
259
256
|
|
260
|
-
|
257
|
+
#: (Prism::ModuleNode node) -> void
|
261
258
|
def on_module_node_enter(node)
|
262
259
|
constant_path = node.constant_path
|
263
260
|
|
@@ -278,12 +275,12 @@ module RubyLsp
|
|
278
275
|
end
|
279
276
|
end
|
280
277
|
|
281
|
-
|
278
|
+
#: (Prism::ImplicitNode node) -> void
|
282
279
|
def on_implicit_node_enter(node)
|
283
280
|
@inside_implicit_node = true
|
284
281
|
end
|
285
282
|
|
286
|
-
|
283
|
+
#: (Prism::ImplicitNode node) -> void
|
287
284
|
def on_implicit_node_leave(node)
|
288
285
|
@inside_implicit_node = false
|
289
286
|
end
|
@@ -292,12 +289,12 @@ module RubyLsp
|
|
292
289
|
|
293
290
|
# Textmate provides highlighting for a subset of these special Ruby-specific methods. We want to utilize that
|
294
291
|
# highlighting, so we avoid making a semantic token for it.
|
295
|
-
|
292
|
+
#: (String method_name) -> bool
|
296
293
|
def special_method?(method_name)
|
297
294
|
SPECIAL_RUBY_METHODS.include?(method_name)
|
298
295
|
end
|
299
296
|
|
300
|
-
|
297
|
+
#: (Prism::CallNode node) -> void
|
301
298
|
def process_regexp_locals(node)
|
302
299
|
receiver = node.receiver
|
303
300
|
|
@@ -310,7 +307,8 @@ module RubyLsp
|
|
310
307
|
# For each capture name we find in the regexp, look for a local in the current_scope
|
311
308
|
Regexp.new(content, Regexp::FIXEDENCODING).names.each do |name|
|
312
309
|
# The +3 is to compensate for the "(?<" part of the capture name
|
313
|
-
|
310
|
+
capture_name_index = content.index("(?<#{name}>") #: as !nil
|
311
|
+
capture_name_offset = capture_name_index + 3
|
314
312
|
local_var_loc = loc.copy(start_offset: loc.start_offset + capture_name_offset, length: name.length)
|
315
313
|
|
316
314
|
local = @current_scope.lookup(name)
|
@@ -4,31 +4,22 @@
|
|
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, 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
|
-
return if
|
22
|
+
return if @sorbet_level.true_or_higher?
|
32
23
|
|
33
24
|
message = node.message
|
34
25
|
return unless message
|
@@ -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 || []
|
@@ -80,7 +69,11 @@ module RubyLsp
|
|
80
69
|
signature.matches?(arguments)
|
81
70
|
end || 0
|
82
71
|
|
83
|
-
parameter_length = [
|
72
|
+
parameter_length = [
|
73
|
+
signatures[active_sig_index] #: as !nil
|
74
|
+
.parameters.length - 1,
|
75
|
+
0,
|
76
|
+
].max
|
84
77
|
active_parameter = (arguments.length - 1).clamp(0, parameter_length)
|
85
78
|
|
86
79
|
# If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
|
@@ -93,15 +86,7 @@ module RubyLsp
|
|
93
86
|
[active_sig_index, active_parameter]
|
94
87
|
end
|
95
88
|
|
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
|
89
|
+
#: (Array[RubyIndexer::Entry::Signature] signatures, String method_name, Array[RubyIndexer::Entry] methods, String title, String? extra_links) -> Array[Interface::SignatureInformation]
|
105
90
|
def generate_signatures(signatures, method_name, methods, title, extra_links)
|
106
91
|
signatures.map do |signature|
|
107
92
|
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
|
+
#: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
|
8
|
+
def initialize(response_builder, global_state, dispatcher, uri)
|
9
|
+
super
|
10
|
+
|
11
|
+
@describe_block_nesting = [] #: Array[String]
|
12
|
+
@spec_class_stack = [] #: Array[bool]
|
13
|
+
|
14
|
+
dispatcher.register(
|
15
|
+
self,
|
16
|
+
# Common handlers registered in parent class
|
17
|
+
:on_class_node_enter,
|
18
|
+
:on_call_node_enter, # e.g. `describe` or `it`
|
19
|
+
:on_call_node_leave,
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
#: (Prism::ClassNode) -> void
|
24
|
+
def on_class_node_enter(node)
|
25
|
+
with_test_ancestor_tracking(node) do |_, ancestors|
|
26
|
+
is_spec = ancestors.include?("Minitest::Spec")
|
27
|
+
@spec_class_stack.push(is_spec)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#: (Prism::ClassNode) -> void
|
32
|
+
def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
33
|
+
super
|
34
|
+
|
35
|
+
@spec_class_stack.pop
|
36
|
+
end
|
37
|
+
|
38
|
+
#: (Prism::CallNode) -> void
|
39
|
+
def on_call_node_enter(node)
|
40
|
+
case node.name
|
41
|
+
when :describe
|
42
|
+
handle_describe(node)
|
43
|
+
when :it, :specify
|
44
|
+
handle_example(node)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#: (Prism::CallNode) -> void
|
49
|
+
def on_call_node_leave(node)
|
50
|
+
return unless node.name == :describe && !node.receiver
|
51
|
+
|
52
|
+
@describe_block_nesting.pop
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
#: (Prism::CallNode) -> void
|
58
|
+
def handle_describe(node)
|
59
|
+
return if node.block.nil?
|
60
|
+
|
61
|
+
description = extract_description(node)
|
62
|
+
return unless description
|
63
|
+
|
64
|
+
return unless in_spec_context?
|
65
|
+
|
66
|
+
if @nesting.empty? && @describe_block_nesting.empty?
|
67
|
+
test_item = Requests::Support::TestItem.new(
|
68
|
+
description,
|
69
|
+
description,
|
70
|
+
@uri,
|
71
|
+
range_from_node(node),
|
72
|
+
framework: :minitest,
|
73
|
+
)
|
74
|
+
@response_builder.add(test_item)
|
75
|
+
@response_builder.add_code_lens(test_item)
|
76
|
+
else
|
77
|
+
add_to_parent_test_group(description, node)
|
78
|
+
end
|
79
|
+
|
80
|
+
@describe_block_nesting << description
|
81
|
+
end
|
82
|
+
|
83
|
+
#: (Prism::CallNode) -> void
|
84
|
+
def handle_example(node)
|
85
|
+
return unless in_spec_context?
|
86
|
+
|
87
|
+
return if @describe_block_nesting.empty? && @nesting.empty?
|
88
|
+
|
89
|
+
description = extract_description(node)
|
90
|
+
return unless description
|
91
|
+
|
92
|
+
add_to_parent_test_group(description, node)
|
93
|
+
end
|
94
|
+
|
95
|
+
#: (String, Prism::CallNode) -> void
|
96
|
+
def add_to_parent_test_group(description, node)
|
97
|
+
parent_test_group = find_parent_test_group
|
98
|
+
return unless parent_test_group
|
99
|
+
|
100
|
+
test_item = Requests::Support::TestItem.new(
|
101
|
+
description,
|
102
|
+
description,
|
103
|
+
@uri,
|
104
|
+
range_from_node(node),
|
105
|
+
framework: :minitest,
|
106
|
+
)
|
107
|
+
parent_test_group.add(test_item)
|
108
|
+
@response_builder.add_code_lens(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
|
+
#: (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
|
+
@spec_class_stack.last #: as !nil
|
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
|