ruby-lsp 0.23.15 → 0.26.9
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/VERSION +1 -1
- data/exe/ruby-lsp +17 -14
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +41 -14
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -3
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +42 -20
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -7
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +49 -62
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +84 -74
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -9
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +9 -14
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +4 -4
- data/lib/ruby_lsp/addon.rb +44 -15
- data/lib/ruby_lsp/base_server.rb +56 -37
- data/lib/ruby_lsp/client_capabilities.rb +6 -1
- data/lib/ruby_lsp/document.rb +174 -62
- data/lib/ruby_lsp/erb_document.rb +10 -8
- data/lib/ruby_lsp/global_state.rb +86 -33
- data/lib/ruby_lsp/internal.rb +6 -3
- data/lib/ruby_lsp/listeners/completion.rb +22 -11
- data/lib/ruby_lsp/listeners/definition.rb +41 -21
- data/lib/ruby_lsp/listeners/document_highlight.rb +26 -1
- data/lib/ruby_lsp/listeners/document_link.rb +64 -28
- data/lib/ruby_lsp/listeners/hover.rb +27 -16
- data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +2 -2
- data/lib/ruby_lsp/listeners/signature_help.rb +2 -2
- data/lib/ruby_lsp/listeners/spec_style.rb +155 -79
- data/lib/ruby_lsp/listeners/test_discovery.rb +39 -21
- data/lib/ruby_lsp/listeners/test_style.rb +75 -35
- data/lib/ruby_lsp/rbs_document.rb +3 -6
- data/lib/ruby_lsp/requests/code_action_resolve.rb +83 -58
- data/lib/ruby_lsp/requests/code_actions.rb +20 -5
- data/lib/ruby_lsp/requests/code_lens.rb +27 -6
- data/lib/ruby_lsp/requests/completion.rb +3 -3
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
- data/lib/ruby_lsp/requests/definition.rb +4 -7
- data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
- data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
- data/lib/ruby_lsp/requests/document_link.rb +1 -1
- data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +64 -12
- data/lib/ruby_lsp/requests/hover.rb +3 -6
- data/lib/ruby_lsp/requests/inlay_hints.rb +4 -4
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +10 -21
- data/lib/ruby_lsp/requests/rename.rb +9 -10
- data/lib/ruby_lsp/requests/request.rb +8 -8
- data/lib/ruby_lsp/requests/selection_ranges.rb +2 -2
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +2 -2
- data/lib/ruby_lsp/requests/signature_help.rb +2 -2
- data/lib/ruby_lsp/requests/support/annotation.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +9 -12
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +7 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -3
- data/lib/ruby_lsp/requests/support/source_uri.rb +7 -4
- data/lib/ruby_lsp/requests/support/test_item.rb +7 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +20 -12
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -4
- data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -3
- data/lib/ruby_lsp/response_builders/hover.rb +1 -4
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +4 -5
- data/lib/ruby_lsp/response_builders/signature_help.rb +1 -2
- data/lib/ruby_lsp/response_builders/test_collection.rb +29 -3
- data/lib/ruby_lsp/ruby_document.rb +14 -42
- data/lib/ruby_lsp/scripts/compose_bundle.rb +3 -3
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +3 -1
- data/lib/ruby_lsp/server.rb +173 -130
- data/lib/ruby_lsp/setup_bundler.rb +114 -47
- data/lib/ruby_lsp/static_docs.rb +1 -0
- data/lib/ruby_lsp/store.rb +6 -16
- data/lib/ruby_lsp/test_helper.rb +1 -4
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +121 -17
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +65 -25
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +16 -18
- data/lib/ruby_lsp/utils.rb +102 -13
- data/static_docs/break.md +103 -0
- metadata +8 -33
- data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
- data/lib/ruby_indexer/test/configuration_test.rb +0 -280
- data/lib/ruby_indexer/test/constant_test.rb +0 -402
- data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
- data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
- data/lib/ruby_indexer/test/index_test.rb +0 -2190
- data/lib/ruby_indexer/test/instance_variables_test.rb +0 -240
- data/lib/ruby_indexer/test/method_test.rb +0 -973
- data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -380
- data/lib/ruby_indexer/test/reference_finder_test.rb +0 -330
- data/lib/ruby_indexer/test/test_case.rb +0 -51
- data/lib/ruby_indexer/test/uri_test.rb +0 -85
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
|
@@ -4,151 +4,227 @@
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
module Listeners
|
|
6
6
|
class SpecStyle < TestDiscovery
|
|
7
|
-
|
|
7
|
+
class Group
|
|
8
|
+
#: String
|
|
9
|
+
attr_reader :id
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
#: (String) -> void
|
|
12
|
+
def initialize(id)
|
|
13
|
+
@id = id
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class ClassGroup < Group; end
|
|
18
|
+
class DescribeGroup < Group; end
|
|
19
|
+
|
|
20
|
+
#: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
|
|
10
21
|
def initialize(response_builder, global_state, dispatcher, uri)
|
|
11
|
-
super
|
|
22
|
+
super(response_builder, global_state, uri)
|
|
12
23
|
|
|
13
|
-
@
|
|
14
|
-
@spec_class_stack = [] #: Array[bool]
|
|
24
|
+
@spec_group_id_stack = [] #: Array[Group?]
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Common handlers registered in parent class
|
|
26
|
+
register_events(
|
|
27
|
+
dispatcher,
|
|
19
28
|
:on_class_node_enter,
|
|
20
|
-
:on_call_node_enter,
|
|
29
|
+
:on_call_node_enter,
|
|
21
30
|
:on_call_node_leave,
|
|
22
31
|
)
|
|
23
32
|
end
|
|
24
33
|
|
|
25
|
-
#: (
|
|
26
|
-
def on_class_node_enter(node)
|
|
27
|
-
with_test_ancestor_tracking(node) do |
|
|
28
|
-
|
|
29
|
-
@spec_class_stack.push(is_spec)
|
|
34
|
+
#: (Prism::ClassNode) -> void
|
|
35
|
+
def on_class_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
36
|
+
with_test_ancestor_tracking(node) do |name, ancestors|
|
|
37
|
+
@spec_group_id_stack << (ancestors.include?("Minitest::Spec") ? ClassGroup.new(name) : nil)
|
|
30
38
|
end
|
|
31
39
|
end
|
|
32
40
|
|
|
33
|
-
#: (
|
|
41
|
+
#: (Prism::ClassNode) -> void
|
|
34
42
|
def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
43
|
+
@spec_group_id_stack.pop
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#: (Prism::ModuleNode) -> void
|
|
48
|
+
def on_module_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
49
|
+
@spec_group_id_stack << nil
|
|
35
50
|
super
|
|
51
|
+
end
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
#: (Prism::ModuleNode) -> void
|
|
54
|
+
def on_module_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
55
|
+
@spec_group_id_stack.pop
|
|
56
|
+
super
|
|
38
57
|
end
|
|
39
58
|
|
|
40
|
-
#: (
|
|
41
|
-
def on_call_node_enter(node)
|
|
59
|
+
#: (Prism::CallNode) -> void
|
|
60
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
42
61
|
case node.name
|
|
43
62
|
when :describe
|
|
44
63
|
handle_describe(node)
|
|
45
64
|
when :it, :specify
|
|
46
|
-
handle_example(node)
|
|
65
|
+
handle_example(node) if in_spec_context?
|
|
47
66
|
end
|
|
48
67
|
end
|
|
49
68
|
|
|
50
|
-
#: (
|
|
51
|
-
def on_call_node_leave(node)
|
|
69
|
+
#: (Prism::CallNode) -> void
|
|
70
|
+
def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
52
71
|
return unless node.name == :describe && !node.receiver
|
|
53
72
|
|
|
54
|
-
@
|
|
73
|
+
current_group = @spec_group_id_stack.last
|
|
74
|
+
return unless current_group.is_a?(DescribeGroup)
|
|
75
|
+
|
|
76
|
+
description = extract_description(node)
|
|
77
|
+
return unless description && current_group.id.end_with?(description)
|
|
78
|
+
|
|
79
|
+
@spec_group_id_stack.pop
|
|
55
80
|
end
|
|
56
81
|
|
|
57
82
|
private
|
|
58
83
|
|
|
59
|
-
#: (
|
|
84
|
+
#: (Prism::CallNode) -> void
|
|
60
85
|
def handle_describe(node)
|
|
86
|
+
# Describes will include the nesting of all classes and all outer describes as part of its ID, unlike classes
|
|
87
|
+
# that ignore describes
|
|
61
88
|
return if node.block.nil?
|
|
62
89
|
|
|
63
90
|
description = extract_description(node)
|
|
64
91
|
return unless description
|
|
65
92
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@uri,
|
|
73
|
-
range_from_node(node),
|
|
74
|
-
framework: :minitest,
|
|
75
|
-
)
|
|
76
|
-
@response_builder.add(test_item)
|
|
93
|
+
parent = latest_group
|
|
94
|
+
return unless parent
|
|
95
|
+
|
|
96
|
+
id = case parent
|
|
97
|
+
when Requests::Support::TestItem
|
|
98
|
+
"#{parent.id}::#{description}"
|
|
77
99
|
else
|
|
78
|
-
|
|
100
|
+
description
|
|
79
101
|
end
|
|
80
102
|
|
|
81
|
-
|
|
103
|
+
test_item = Requests::Support::TestItem.new(
|
|
104
|
+
id,
|
|
105
|
+
description,
|
|
106
|
+
@uri,
|
|
107
|
+
range_from_node(node),
|
|
108
|
+
framework: :minitest,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
parent.add(test_item)
|
|
112
|
+
@response_builder.add_code_lens(test_item)
|
|
113
|
+
@spec_group_id_stack << DescribeGroup.new(id)
|
|
82
114
|
end
|
|
83
115
|
|
|
84
|
-
#: (
|
|
116
|
+
#: (Prism::CallNode) -> void
|
|
85
117
|
def handle_example(node)
|
|
86
|
-
|
|
118
|
+
description = extract_it_description(node)
|
|
119
|
+
line = node.location.start_line - 1
|
|
120
|
+
parent = latest_group
|
|
121
|
+
return unless parent.is_a?(Requests::Support::TestItem)
|
|
87
122
|
|
|
88
|
-
|
|
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
|
|
123
|
+
id = "#{parent.id}##{format("test_%04d_%s", line, description)}"
|
|
100
124
|
|
|
101
125
|
test_item = Requests::Support::TestItem.new(
|
|
102
|
-
|
|
126
|
+
id,
|
|
103
127
|
description,
|
|
104
128
|
@uri,
|
|
105
129
|
range_from_node(node),
|
|
106
130
|
framework: :minitest,
|
|
107
131
|
)
|
|
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
132
|
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
parent.add(test_item)
|
|
134
|
+
@response_builder.add_code_lens(test_item)
|
|
135
|
+
end
|
|
122
136
|
|
|
123
|
-
|
|
137
|
+
#: (Prism::CallNode) -> String?
|
|
138
|
+
def extract_description(node)
|
|
139
|
+
arguments = node.arguments&.arguments
|
|
140
|
+
return unless arguments
|
|
124
141
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
end
|
|
142
|
+
parts = arguments.map { |arg| extract_argument_content(arg) }
|
|
143
|
+
return if parts.empty?
|
|
128
144
|
|
|
129
|
-
|
|
145
|
+
parts.join("::")
|
|
130
146
|
end
|
|
131
147
|
|
|
132
|
-
#: (
|
|
133
|
-
def
|
|
148
|
+
#: (Prism::CallNode) -> String
|
|
149
|
+
def extract_it_description(node)
|
|
150
|
+
# Minitest formats the descriptions into test method names by using the count of examples with the description
|
|
151
|
+
# We are not guaranteed to discover examples in the exact order using static analysis, so we use the line number
|
|
152
|
+
# instead. Note that anonymous examples mixed with meta-programming will not be handled correctly
|
|
134
153
|
first_argument = node.arguments&.arguments&.first
|
|
135
|
-
return unless first_argument
|
|
154
|
+
return "anonymous" unless first_argument
|
|
155
|
+
|
|
156
|
+
extract_argument_content(first_argument) || "anonymous"
|
|
157
|
+
end
|
|
136
158
|
|
|
137
|
-
|
|
159
|
+
#: (Prism::Node) -> String?
|
|
160
|
+
def extract_argument_content(arg)
|
|
161
|
+
case arg
|
|
138
162
|
when Prism::StringNode
|
|
139
|
-
|
|
163
|
+
arg.content
|
|
164
|
+
when Prism::SymbolNode
|
|
165
|
+
arg.value
|
|
140
166
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
141
|
-
constant_name(
|
|
167
|
+
constant_name(arg)
|
|
142
168
|
else
|
|
143
|
-
|
|
169
|
+
arg.slice
|
|
144
170
|
end
|
|
145
171
|
end
|
|
146
172
|
|
|
173
|
+
#: -> (Requests::Support::TestItem | ResponseBuilders::TestCollection)?
|
|
174
|
+
def latest_group
|
|
175
|
+
# If we haven't found anything yet, then return the response builder
|
|
176
|
+
return @response_builder if @spec_group_id_stack.compact.empty?
|
|
177
|
+
# If we found something that isn't a group last, then we're inside a random module or class, but not a spec
|
|
178
|
+
# group
|
|
179
|
+
return unless @spec_group_id_stack.last
|
|
180
|
+
|
|
181
|
+
# Specs using at least one class as a group require special handling
|
|
182
|
+
closest_class_index = @spec_group_id_stack.rindex { |i| i.is_a?(ClassGroup) }
|
|
183
|
+
|
|
184
|
+
if closest_class_index
|
|
185
|
+
first_class_index = @spec_group_id_stack.index { |i| i.is_a?(ClassGroup) } #: as !nil
|
|
186
|
+
first_class = @spec_group_id_stack[first_class_index] #: as !nil
|
|
187
|
+
item = @response_builder[first_class.id] #: as !nil
|
|
188
|
+
|
|
189
|
+
# Descend into child items from the beginning all the way to the latest class group, ignoring describes
|
|
190
|
+
@spec_group_id_stack[first_class_index + 1..closest_class_index] #: as !nil
|
|
191
|
+
.each do |group|
|
|
192
|
+
next unless group.is_a?(ClassGroup)
|
|
193
|
+
|
|
194
|
+
item = item[group.id] #: as !nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# From the class forward, we must take describes into account
|
|
198
|
+
@spec_group_id_stack[closest_class_index + 1..] #: as !nil
|
|
199
|
+
.each do |group|
|
|
200
|
+
next unless group
|
|
201
|
+
|
|
202
|
+
item = item[group.id] #: as !nil
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
return item
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Specs only using describes
|
|
209
|
+
first_group_index = @spec_group_id_stack.index { |i| i.is_a?(DescribeGroup) }
|
|
210
|
+
return unless first_group_index
|
|
211
|
+
|
|
212
|
+
first_group = @spec_group_id_stack[first_group_index] #: as !nil
|
|
213
|
+
item = @response_builder[first_group.id] #: as !nil
|
|
214
|
+
|
|
215
|
+
@spec_group_id_stack[first_group_index + 1..] #: as !nil
|
|
216
|
+
.each do |group|
|
|
217
|
+
next unless group.is_a?(DescribeGroup)
|
|
218
|
+
|
|
219
|
+
item = item[group.id] #: as !nil
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
item
|
|
223
|
+
end
|
|
224
|
+
|
|
147
225
|
#: -> bool
|
|
148
226
|
def in_spec_context?
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
@spec_class_stack.last #: as !nil
|
|
227
|
+
@nesting.empty? || @spec_group_id_stack.any? { |id| id }
|
|
152
228
|
end
|
|
153
229
|
end
|
|
154
230
|
end
|
|
@@ -3,32 +3,23 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
module Listeners
|
|
6
|
+
# @abstract
|
|
6
7
|
class TestDiscovery
|
|
7
|
-
extend T::Helpers
|
|
8
|
-
abstract!
|
|
9
|
-
|
|
10
8
|
include Requests::Support::Common
|
|
11
9
|
|
|
12
10
|
DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>"
|
|
13
11
|
|
|
14
|
-
#: (ResponseBuilders::TestCollection response_builder, GlobalState global_state,
|
|
15
|
-
def initialize(response_builder, global_state,
|
|
12
|
+
#: (ResponseBuilders::TestCollection response_builder, GlobalState global_state, URI::Generic uri) -> void
|
|
13
|
+
def initialize(response_builder, global_state, uri)
|
|
16
14
|
@response_builder = response_builder
|
|
17
15
|
@uri = uri
|
|
18
16
|
@index = global_state.index #: RubyIndexer::Index
|
|
19
17
|
@visibility_stack = [:public] #: Array[Symbol]
|
|
20
18
|
@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
19
|
end
|
|
29
20
|
|
|
30
21
|
#: (Prism::ModuleNode node) -> void
|
|
31
|
-
def on_module_node_enter(node)
|
|
22
|
+
def on_module_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
32
23
|
@visibility_stack << :public
|
|
33
24
|
|
|
34
25
|
name = constant_name(node.constant_path)
|
|
@@ -38,19 +29,31 @@ module RubyLsp
|
|
|
38
29
|
end
|
|
39
30
|
|
|
40
31
|
#: (Prism::ModuleNode node) -> void
|
|
41
|
-
def on_module_node_leave(node)
|
|
32
|
+
def on_module_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
42
33
|
@visibility_stack.pop
|
|
43
34
|
@nesting.pop
|
|
44
35
|
end
|
|
45
36
|
|
|
46
37
|
#: (Prism::ClassNode node) -> void
|
|
47
|
-
def on_class_node_leave(node)
|
|
38
|
+
def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
48
39
|
@visibility_stack.pop
|
|
49
40
|
@nesting.pop
|
|
50
41
|
end
|
|
51
42
|
|
|
52
43
|
private
|
|
53
44
|
|
|
45
|
+
#: (Prism::Dispatcher, *Symbol) -> void
|
|
46
|
+
def register_events(dispatcher, *events)
|
|
47
|
+
unique_events = events.dup.push(
|
|
48
|
+
:on_class_node_leave,
|
|
49
|
+
:on_module_node_enter,
|
|
50
|
+
:on_module_node_leave,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
unique_events.uniq!
|
|
54
|
+
dispatcher.register(self, *unique_events)
|
|
55
|
+
end
|
|
56
|
+
|
|
54
57
|
#: (String? name) -> String
|
|
55
58
|
def calc_fully_qualified_name(name)
|
|
56
59
|
RubyIndexer::Index.actual_nesting(@nesting, name).join("::")
|
|
@@ -58,11 +61,26 @@ module RubyLsp
|
|
|
58
61
|
|
|
59
62
|
#: (Prism::ClassNode node, String fully_qualified_name) -> Array[String]
|
|
60
63
|
def calc_attached_ancestors(node, fully_qualified_name)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
superclass = node.superclass
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
ancestors = @index.linearized_ancestors_of(fully_qualified_name)
|
|
68
|
+
# If the project has no bundle and a test class inherits from `Minitest::Test`, the linearized ancestors will
|
|
69
|
+
# not include the parent class because we never indexed it in the first place. Here we add the superclass
|
|
70
|
+
# directly, so that we can support running tests in projects without a bundle
|
|
71
|
+
return ancestors if ancestors.length > 1
|
|
72
|
+
|
|
73
|
+
# If all we found is the class itself, then manually include the parent class
|
|
74
|
+
if ancestors.first == fully_qualified_name && superclass
|
|
75
|
+
return [*ancestors, superclass.slice]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
ancestors
|
|
79
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
80
|
+
# When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
|
|
81
|
+
# provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
|
|
82
|
+
[superclass&.slice].compact
|
|
83
|
+
end
|
|
66
84
|
end
|
|
67
85
|
|
|
68
86
|
#: (Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode node) -> String
|
|
@@ -71,7 +89,7 @@ module RubyLsp
|
|
|
71
89
|
slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
|
|
72
90
|
end
|
|
73
91
|
|
|
74
|
-
#: (Prism::ClassNode node
|
|
92
|
+
#: (Prism::ClassNode node) { (String name, Array[String] ancestors) -> void } -> void
|
|
75
93
|
def with_test_ancestor_tracking(node, &block)
|
|
76
94
|
@visibility_stack << :public
|
|
77
95
|
name = constant_name(node.constant_path)
|
|
@@ -33,18 +33,20 @@ module RubyLsp
|
|
|
33
33
|
|
|
34
34
|
if tags.include?("test_dir")
|
|
35
35
|
if children.empty?
|
|
36
|
-
full_files.concat(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
full_files.concat(
|
|
37
|
+
Dir.glob(
|
|
38
|
+
"#{path}/**/{*_test,test_*,*_spec}.rb",
|
|
39
|
+
File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
|
|
40
|
+
).map! { |p| Shellwords.escape(p) },
|
|
41
|
+
)
|
|
40
42
|
end
|
|
41
43
|
elsif tags.include?("test_file")
|
|
42
|
-
full_files << path if children.empty?
|
|
44
|
+
full_files << Shellwords.escape(path) if children.empty?
|
|
43
45
|
elsif tags.include?("test_group")
|
|
44
46
|
# If all of the children of the current test group are other groups, then there's no need to add it to the
|
|
45
47
|
# aggregated examples
|
|
46
48
|
unless children.any? && children.all? { |child| child[:tags].include?("test_group") }
|
|
47
|
-
aggregated_tests[path][item[:
|
|
49
|
+
aggregated_tests[path][item[:id]] = { tags: tags, examples: [] }
|
|
48
50
|
end
|
|
49
51
|
else
|
|
50
52
|
class_name, method_name = item[:id].split("#")
|
|
@@ -74,7 +76,10 @@ module RubyLsp
|
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
unless full_files.empty?
|
|
77
|
-
|
|
79
|
+
specs, tests = full_files.partition { |path| spec?(path) }
|
|
80
|
+
|
|
81
|
+
commands << "#{COMMAND} -Itest -e \"ARGV.each { |f| require f }\" #{tests.join(" ")}" if tests.any?
|
|
82
|
+
commands << "#{COMMAND} -Ispec -e \"ARGV.each { |f| require f }\" #{specs.join(" ")}" if specs.any?
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
commands
|
|
@@ -82,10 +87,15 @@ module RubyLsp
|
|
|
82
87
|
|
|
83
88
|
private
|
|
84
89
|
|
|
90
|
+
#: (String) -> bool
|
|
91
|
+
def spec?(path)
|
|
92
|
+
File.fnmatch?("**/spec/**/*_spec.rb", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
93
|
+
end
|
|
94
|
+
|
|
85
95
|
#: (String, Hash[String, Hash[Symbol, untyped]]) -> String
|
|
86
96
|
def handle_minitest_groups(file_path, groups_and_examples)
|
|
87
97
|
regexes = groups_and_examples.flat_map do |group, info|
|
|
88
|
-
examples = info[:examples]
|
|
98
|
+
examples = info[:examples].map { |e| Shellwords.escape(e).gsub(/test_\d{4}/, "test_\\d{4}") }
|
|
89
99
|
group_regex = Shellwords.escape(group).gsub(
|
|
90
100
|
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
|
91
101
|
".*",
|
|
@@ -93,9 +103,9 @@ module RubyLsp
|
|
|
93
103
|
if examples.empty?
|
|
94
104
|
"^#{group_regex}(#|::)"
|
|
95
105
|
elsif examples.length == 1
|
|
96
|
-
"^#{group_regex}##{examples[0]}
|
|
106
|
+
"^#{group_regex}##{examples[0]}\\$"
|
|
97
107
|
else
|
|
98
|
-
"^#{group_regex}#(#{examples.join("|")})
|
|
108
|
+
"^#{group_regex}#(#{examples.join("|")})\\$"
|
|
99
109
|
end
|
|
100
110
|
end
|
|
101
111
|
|
|
@@ -105,7 +115,8 @@ module RubyLsp
|
|
|
105
115
|
"(#{regexes.join("|")})"
|
|
106
116
|
end
|
|
107
117
|
|
|
108
|
-
|
|
118
|
+
load_path = spec?(file_path) ? "-Ispec" : "-Itest"
|
|
119
|
+
"#{COMMAND} #{load_path} #{Shellwords.escape(file_path)} --name \"/#{regex}/\""
|
|
109
120
|
end
|
|
110
121
|
|
|
111
122
|
#: (String, Hash[String, Hash[Symbol, untyped]]) -> Array[String]
|
|
@@ -116,13 +127,13 @@ module RubyLsp
|
|
|
116
127
|
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
|
117
128
|
".*",
|
|
118
129
|
)
|
|
119
|
-
command = +"#{
|
|
130
|
+
command = +"#{COMMAND} -Itest #{Shellwords.escape(file_path)} --testcase \"/^#{group_regex}\\$/\""
|
|
120
131
|
|
|
121
132
|
unless examples.empty?
|
|
122
133
|
command << if examples.length == 1
|
|
123
|
-
" --name \"/#{examples[0]}
|
|
134
|
+
" --name \"/#{examples[0]}\\$/\""
|
|
124
135
|
else
|
|
125
|
-
" --name \"/(#{examples.join("|")})
|
|
136
|
+
" --name \"/(#{examples.join("|")})\\$/\""
|
|
126
137
|
end
|
|
127
138
|
end
|
|
128
139
|
|
|
@@ -135,23 +146,24 @@ module RubyLsp
|
|
|
135
146
|
|
|
136
147
|
MINITEST_REPORTER_PATH = File.expand_path("../test_reporters/minitest_reporter.rb", __dir__) #: String
|
|
137
148
|
TEST_UNIT_REPORTER_PATH = File.expand_path("../test_reporters/test_unit_reporter.rb", __dir__) #: String
|
|
138
|
-
ACCESS_MODIFIERS = [:public, :private, :protected].freeze
|
|
139
149
|
BASE_COMMAND = begin
|
|
140
|
-
Bundler.
|
|
150
|
+
Bundler.with_unbundled_env { Bundler.default_lockfile }
|
|
141
151
|
"bundle exec ruby"
|
|
142
152
|
rescue Bundler::GemfileNotFound
|
|
143
153
|
"ruby"
|
|
144
154
|
end #: String
|
|
155
|
+
COMMAND = "#{BASE_COMMAND} -r#{MINITEST_REPORTER_PATH} -r#{TEST_UNIT_REPORTER_PATH}" #: String
|
|
156
|
+
ACCESS_MODIFIERS = [:public, :private, :protected].freeze
|
|
145
157
|
|
|
146
|
-
#: (ResponseBuilders::TestCollection
|
|
158
|
+
#: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
|
|
147
159
|
def initialize(response_builder, global_state, dispatcher, uri)
|
|
148
|
-
super
|
|
160
|
+
super(response_builder, global_state, uri)
|
|
149
161
|
|
|
150
162
|
@framework = :minitest #: Symbol
|
|
163
|
+
@parent_stack = [@response_builder] #: Array[(Requests::Support::TestItem | ResponseBuilders::TestCollection)?]
|
|
151
164
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# Common handlers registered in parent class
|
|
165
|
+
register_events(
|
|
166
|
+
dispatcher,
|
|
155
167
|
:on_class_node_enter,
|
|
156
168
|
:on_def_node_enter,
|
|
157
169
|
:on_call_node_enter,
|
|
@@ -160,48 +172,70 @@ module RubyLsp
|
|
|
160
172
|
end
|
|
161
173
|
|
|
162
174
|
#: (Prism::ClassNode node) -> void
|
|
163
|
-
def on_class_node_enter(node)
|
|
175
|
+
def on_class_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
164
176
|
with_test_ancestor_tracking(node) do |name, ancestors|
|
|
165
177
|
@framework = :test_unit if ancestors.include?("Test::Unit::TestCase")
|
|
166
178
|
|
|
167
179
|
if @framework == :test_unit || non_declarative_minitest?(ancestors, name)
|
|
168
|
-
|
|
180
|
+
test_item = Requests::Support::TestItem.new(
|
|
169
181
|
name,
|
|
170
182
|
name,
|
|
171
183
|
@uri,
|
|
172
184
|
range_from_node(node),
|
|
173
185
|
framework: @framework,
|
|
174
|
-
)
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
last_test_group.add(test_item)
|
|
189
|
+
@response_builder.add_code_lens(test_item)
|
|
190
|
+
@parent_stack << test_item
|
|
191
|
+
else
|
|
192
|
+
@parent_stack << nil
|
|
175
193
|
end
|
|
176
194
|
end
|
|
177
195
|
end
|
|
178
196
|
|
|
197
|
+
#: (Prism::ClassNode node) -> void
|
|
198
|
+
def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
199
|
+
@parent_stack.pop
|
|
200
|
+
super
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
#: (Prism::ModuleNode node) -> void
|
|
204
|
+
def on_module_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
205
|
+
@parent_stack << nil
|
|
206
|
+
super
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
#: (Prism::ModuleNode node) -> void
|
|
210
|
+
def on_module_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
211
|
+
@parent_stack.pop
|
|
212
|
+
super
|
|
213
|
+
end
|
|
214
|
+
|
|
179
215
|
#: (Prism::DefNode node) -> void
|
|
180
|
-
def on_def_node_enter(node)
|
|
216
|
+
def on_def_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
181
217
|
return if @visibility_stack.last != :public
|
|
182
218
|
|
|
183
219
|
name = node.name.to_s
|
|
184
220
|
return unless name.start_with?("test_")
|
|
185
221
|
|
|
186
222
|
current_group_name = RubyIndexer::Index.actual_nesting(@nesting, nil).join("::")
|
|
223
|
+
parent = @parent_stack.last
|
|
224
|
+
return unless parent.is_a?(Requests::Support::TestItem)
|
|
187
225
|
|
|
188
|
-
|
|
189
|
-
# previously pushed and thus we return early and avoid adding items for a framework this listener is not
|
|
190
|
-
# interested in
|
|
191
|
-
test_item = @response_builder[current_group_name]
|
|
192
|
-
return unless test_item
|
|
193
|
-
|
|
194
|
-
test_item.add(Requests::Support::TestItem.new(
|
|
226
|
+
example_item = Requests::Support::TestItem.new(
|
|
195
227
|
"#{current_group_name}##{name}",
|
|
196
228
|
name,
|
|
197
229
|
@uri,
|
|
198
230
|
range_from_node(node),
|
|
199
231
|
framework: @framework,
|
|
200
|
-
)
|
|
232
|
+
)
|
|
233
|
+
parent.add(example_item)
|
|
234
|
+
@response_builder.add_code_lens(example_item)
|
|
201
235
|
end
|
|
202
236
|
|
|
203
237
|
#: (Prism::CallNode node) -> void
|
|
204
|
-
def on_call_node_enter(node)
|
|
238
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
205
239
|
name = node.name
|
|
206
240
|
return unless ACCESS_MODIFIERS.include?(name)
|
|
207
241
|
|
|
@@ -209,7 +243,7 @@ module RubyLsp
|
|
|
209
243
|
end
|
|
210
244
|
|
|
211
245
|
#: (Prism::CallNode node) -> void
|
|
212
|
-
def on_call_node_leave(node)
|
|
246
|
+
def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
213
247
|
name = node.name
|
|
214
248
|
return unless ACCESS_MODIFIERS.include?(name)
|
|
215
249
|
return unless node.arguments&.arguments
|
|
@@ -219,6 +253,12 @@ module RubyLsp
|
|
|
219
253
|
|
|
220
254
|
private
|
|
221
255
|
|
|
256
|
+
#: -> (Requests::Support::TestItem | ResponseBuilders::TestCollection)
|
|
257
|
+
def last_test_group
|
|
258
|
+
index = @parent_stack.rindex { |i| i } #: as !nil
|
|
259
|
+
@parent_stack[index] #: as Requests::Support::TestItem | ResponseBuilders::TestCollection
|
|
260
|
+
end
|
|
261
|
+
|
|
222
262
|
#: (Array[String] attached_ancestors, String fully_qualified_name) -> bool
|
|
223
263
|
def non_declarative_minitest?(attached_ancestors, fully_qualified_name)
|
|
224
264
|
return false unless attached_ancestors.include?("Minitest::Test")
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
|
+
#: [ParseResultType = Array[RBS::AST::Declarations::Base]]
|
|
5
6
|
class RBSDocument < Document
|
|
6
|
-
extend T::Generic
|
|
7
|
-
|
|
8
|
-
ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
|
|
9
|
-
|
|
10
7
|
#: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
|
|
11
8
|
def initialize(source:, version:, uri:, global_state:)
|
|
12
9
|
@syntax_error = false #: bool
|
|
@@ -36,9 +33,9 @@ module RubyLsp
|
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
# @override
|
|
39
|
-
#: ->
|
|
36
|
+
#: -> Symbol
|
|
40
37
|
def language_id
|
|
41
|
-
|
|
38
|
+
:rbs
|
|
42
39
|
end
|
|
43
40
|
end
|
|
44
41
|
end
|