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
|
@@ -13,14 +13,9 @@ module RubyLsp
|
|
|
13
13
|
NEW_METHOD_NAME = "new_method"
|
|
14
14
|
|
|
15
15
|
class CodeActionError < StandardError; end
|
|
16
|
-
|
|
17
|
-
class
|
|
18
|
-
|
|
19
|
-
EmptySelection = new
|
|
20
|
-
InvalidTargetRange = new
|
|
21
|
-
UnknownCodeAction = new
|
|
22
|
-
end
|
|
23
|
-
end
|
|
16
|
+
class EmptySelectionError < CodeActionError; end
|
|
17
|
+
class InvalidTargetRangeError < CodeActionError; end
|
|
18
|
+
class UnknownCodeActionError < CodeActionError; end
|
|
24
19
|
|
|
25
20
|
#: (RubyDocument document, GlobalState global_state, Hash[Symbol, untyped] code_action) -> void
|
|
26
21
|
def initialize(document, global_state, code_action)
|
|
@@ -31,9 +26,9 @@ module RubyLsp
|
|
|
31
26
|
end
|
|
32
27
|
|
|
33
28
|
# @override
|
|
34
|
-
#: -> (Interface::CodeAction
|
|
29
|
+
#: -> (Interface::CodeAction)
|
|
35
30
|
def perform
|
|
36
|
-
|
|
31
|
+
raise EmptySelectionError, "Invalid selection for refactor" if @document.source.empty?
|
|
37
32
|
|
|
38
33
|
case @code_action[:title]
|
|
39
34
|
when CodeActions::EXTRACT_TO_VARIABLE_TITLE
|
|
@@ -47,26 +42,52 @@ module RubyLsp
|
|
|
47
42
|
CodeActions::CREATE_ATTRIBUTE_ACCESSOR
|
|
48
43
|
create_attribute_accessor
|
|
49
44
|
else
|
|
50
|
-
|
|
45
|
+
raise UnknownCodeActionError, "Unknown code action: #{@code_action[:title]}"
|
|
51
46
|
end
|
|
52
47
|
end
|
|
53
48
|
|
|
54
49
|
private
|
|
55
50
|
|
|
56
|
-
#: -> (Interface::CodeAction
|
|
51
|
+
#: -> (Interface::CodeAction)
|
|
57
52
|
def switch_block_style
|
|
58
53
|
source_range = @code_action.dig(:data, :range)
|
|
59
|
-
|
|
54
|
+
if source_range[:start] == source_range[:end]
|
|
55
|
+
block_context = @document.locate_node(
|
|
56
|
+
source_range[:start],
|
|
57
|
+
node_types: [Prism::BlockNode],
|
|
58
|
+
)
|
|
59
|
+
node = block_context.node
|
|
60
|
+
unless node.is_a?(Prism::BlockNode)
|
|
61
|
+
raise InvalidTargetRangeError, "Cursor is not inside a block"
|
|
62
|
+
end
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
# Find the call node at the block node's start position.
|
|
65
|
+
# This should be the call node whose block the cursor is inside of.
|
|
66
|
+
call_context = RubyDocument.locate(
|
|
67
|
+
@document.ast,
|
|
68
|
+
node.location.cached_start_code_units_offset(@document.code_units_cache),
|
|
69
|
+
node_types: [Prism::CallNode],
|
|
70
|
+
code_units_cache: @document.code_units_cache,
|
|
71
|
+
)
|
|
72
|
+
target = call_context.node
|
|
73
|
+
unless target.is_a?(Prism::CallNode) && target.block == node
|
|
74
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
target = @document.locate_first_within_range(
|
|
78
|
+
@code_action.dig(:data, :range),
|
|
79
|
+
node_types: [Prism::CallNode],
|
|
80
|
+
)
|
|
65
81
|
|
|
66
|
-
|
|
82
|
+
unless target.is_a?(Prism::CallNode)
|
|
83
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
84
|
+
end
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
node = target.block
|
|
87
|
+
unless node.is_a?(Prism::BlockNode)
|
|
88
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
70
91
|
|
|
71
92
|
indentation = " " * target.location.start_column unless node.opening_loc.slice == "do"
|
|
72
93
|
|
|
@@ -91,17 +112,17 @@ module RubyLsp
|
|
|
91
112
|
)
|
|
92
113
|
end
|
|
93
114
|
|
|
94
|
-
#: -> (Interface::CodeAction
|
|
115
|
+
#: -> (Interface::CodeAction)
|
|
95
116
|
def refactor_variable
|
|
96
117
|
source_range = @code_action.dig(:data, :range)
|
|
97
|
-
|
|
118
|
+
raise EmptySelectionError, "Invalid selection for refactor" if source_range[:start] == source_range[:end]
|
|
98
119
|
|
|
99
120
|
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
|
100
121
|
extracted_source = @document.source[start_index...end_index] #: as !nil
|
|
101
122
|
|
|
102
123
|
# Find the closest statements node, so that we place the refactor in a valid position
|
|
103
124
|
node_context = RubyDocument
|
|
104
|
-
.locate(@document.
|
|
125
|
+
.locate(@document.ast,
|
|
105
126
|
start_index,
|
|
106
127
|
node_types: [
|
|
107
128
|
Prism::StatementsNode,
|
|
@@ -111,16 +132,20 @@ module RubyLsp
|
|
|
111
132
|
|
|
112
133
|
closest_statements = node_context.node
|
|
113
134
|
parent_statements = node_context.parent
|
|
114
|
-
|
|
135
|
+
if closest_statements.nil? || closest_statements.child_nodes.compact.empty?
|
|
136
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
137
|
+
end
|
|
115
138
|
|
|
116
139
|
# Find the node with the end line closest to the requested position, so that we can place the refactor
|
|
117
140
|
# immediately after that closest node
|
|
118
|
-
closest_node =
|
|
141
|
+
closest_node = closest_statements.child_nodes.compact.min_by do |node|
|
|
119
142
|
distance = source_range.dig(:start, :line) - (node.location.end_line - 1)
|
|
120
143
|
distance <= 0 ? Float::INFINITY : distance
|
|
121
|
-
end
|
|
144
|
+
end #: as !nil
|
|
122
145
|
|
|
123
|
-
|
|
146
|
+
if closest_node.is_a?(Prism::MissingNode)
|
|
147
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
148
|
+
end
|
|
124
149
|
|
|
125
150
|
closest_node_loc = closest_node.location
|
|
126
151
|
# If the parent expression is a single line block, then we have to extract it inside of the one-line block
|
|
@@ -151,7 +176,9 @@ module RubyLsp
|
|
|
151
176
|
lines = @document.source.lines
|
|
152
177
|
|
|
153
178
|
indentation_line = lines[indentation_line_number]
|
|
154
|
-
|
|
179
|
+
unless indentation_line
|
|
180
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
181
|
+
end
|
|
155
182
|
|
|
156
183
|
indentation = indentation_line[/\A */] #: as !nil
|
|
157
184
|
.size
|
|
@@ -162,7 +189,9 @@ module RubyLsp
|
|
|
162
189
|
}
|
|
163
190
|
|
|
164
191
|
line = lines[target_line]
|
|
165
|
-
|
|
192
|
+
unless line
|
|
193
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
194
|
+
end
|
|
166
195
|
|
|
167
196
|
variable_source = if line.strip.empty?
|
|
168
197
|
"\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
|
|
@@ -190,27 +219,31 @@ module RubyLsp
|
|
|
190
219
|
)
|
|
191
220
|
end
|
|
192
221
|
|
|
193
|
-
#: -> (Interface::CodeAction
|
|
222
|
+
#: -> (Interface::CodeAction)
|
|
194
223
|
def refactor_method
|
|
195
224
|
source_range = @code_action.dig(:data, :range)
|
|
196
|
-
|
|
225
|
+
raise EmptySelectionError, "Invalid selection for refactor" if source_range[:start] == source_range[:end]
|
|
197
226
|
|
|
198
227
|
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
|
199
228
|
extracted_source = @document.source[start_index...end_index] #: as !nil
|
|
200
229
|
|
|
201
230
|
# Find the closest method declaration node, so that we place the refactor in a valid position
|
|
202
231
|
node_context = RubyDocument.locate(
|
|
203
|
-
@document.
|
|
232
|
+
@document.ast,
|
|
204
233
|
start_index,
|
|
205
234
|
node_types: [Prism::DefNode],
|
|
206
235
|
code_units_cache: @document.code_units_cache,
|
|
207
236
|
)
|
|
208
237
|
closest_node = node_context.node
|
|
209
|
-
|
|
238
|
+
unless closest_node
|
|
239
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
240
|
+
end
|
|
210
241
|
|
|
211
242
|
target_range = if closest_node.is_a?(Prism::DefNode)
|
|
212
243
|
end_keyword_loc = closest_node.end_keyword_loc
|
|
213
|
-
|
|
244
|
+
unless end_keyword_loc
|
|
245
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
246
|
+
end
|
|
214
247
|
|
|
215
248
|
end_line = end_keyword_loc.end_line - 1
|
|
216
249
|
character = end_keyword_loc.end_column
|
|
@@ -331,7 +364,7 @@ module RubyLsp
|
|
|
331
364
|
indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
|
|
332
365
|
end
|
|
333
366
|
|
|
334
|
-
#: -> (Interface::CodeAction
|
|
367
|
+
#: -> (Interface::CodeAction)
|
|
335
368
|
def create_attribute_accessor
|
|
336
369
|
source_range = @code_action.dig(:data, :range)
|
|
337
370
|
|
|
@@ -349,20 +382,12 @@ module RubyLsp
|
|
|
349
382
|
)
|
|
350
383
|
node = node_context.node
|
|
351
384
|
|
|
352
|
-
|
|
385
|
+
unless CodeActions::INSTANCE_VARIABLE_NODES.include?(node.class)
|
|
386
|
+
raise EmptySelectionError, "Invalid selection for refactor"
|
|
387
|
+
end
|
|
353
388
|
end
|
|
354
389
|
|
|
355
|
-
node =
|
|
356
|
-
node,
|
|
357
|
-
T.any(
|
|
358
|
-
Prism::InstanceVariableAndWriteNode,
|
|
359
|
-
Prism::InstanceVariableOperatorWriteNode,
|
|
360
|
-
Prism::InstanceVariableOrWriteNode,
|
|
361
|
-
Prism::InstanceVariableReadNode,
|
|
362
|
-
Prism::InstanceVariableTargetNode,
|
|
363
|
-
Prism::InstanceVariableWriteNode,
|
|
364
|
-
),
|
|
365
|
-
)
|
|
390
|
+
node = node #: as Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode
|
|
366
391
|
|
|
367
392
|
node_context = @document.locate_node(
|
|
368
393
|
{
|
|
@@ -376,20 +401,20 @@ module RubyLsp
|
|
|
376
401
|
],
|
|
377
402
|
)
|
|
378
403
|
closest_node = node_context.node
|
|
379
|
-
|
|
404
|
+
if closest_node.nil?
|
|
405
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
406
|
+
end
|
|
380
407
|
|
|
381
408
|
attribute_name = node.name[1..]
|
|
382
409
|
indentation = " " * (closest_node.location.start_column + 2)
|
|
383
|
-
attribute_accessor_source =
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
end,
|
|
392
|
-
)
|
|
410
|
+
attribute_accessor_source = case @code_action[:title]
|
|
411
|
+
when CodeActions::CREATE_ATTRIBUTE_READER
|
|
412
|
+
"#{indentation}attr_reader :#{attribute_name}\n\n"
|
|
413
|
+
when CodeActions::CREATE_ATTRIBUTE_WRITER
|
|
414
|
+
"#{indentation}attr_writer :#{attribute_name}\n\n"
|
|
415
|
+
when CodeActions::CREATE_ATTRIBUTE_ACCESSOR
|
|
416
|
+
"#{indentation}attr_accessor :#{attribute_name}\n\n"
|
|
417
|
+
end #: as !nil
|
|
393
418
|
|
|
394
419
|
target_start_line = closest_node.location.start_line
|
|
395
420
|
target_range = {
|
|
@@ -63,12 +63,8 @@ module RubyLsp
|
|
|
63
63
|
kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
|
|
64
64
|
data: { range: @range, uri: @uri.to_s },
|
|
65
65
|
)
|
|
66
|
-
code_actions << Interface::CodeAction.new(
|
|
67
|
-
title: TOGGLE_BLOCK_STYLE_TITLE,
|
|
68
|
-
kind: Constant::CodeActionKind::REFACTOR_REWRITE,
|
|
69
|
-
data: { range: @range, uri: @uri.to_s },
|
|
70
|
-
)
|
|
71
66
|
end
|
|
67
|
+
code_actions.concat(toggle_block_style_action)
|
|
72
68
|
code_actions.concat(attribute_actions)
|
|
73
69
|
|
|
74
70
|
code_actions
|
|
@@ -113,6 +109,25 @@ module RubyLsp
|
|
|
113
109
|
),
|
|
114
110
|
]
|
|
115
111
|
end
|
|
112
|
+
|
|
113
|
+
#: -> Array[Interface::CodeAction]
|
|
114
|
+
def toggle_block_style_action
|
|
115
|
+
if @range[:start] == @range[:end]
|
|
116
|
+
block_context = @document.locate_node(
|
|
117
|
+
@range[:start],
|
|
118
|
+
node_types: [Prism::BlockNode],
|
|
119
|
+
)
|
|
120
|
+
return [] unless block_context.node.is_a?(Prism::BlockNode)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
[
|
|
124
|
+
Interface::CodeAction.new(
|
|
125
|
+
title: TOGGLE_BLOCK_STYLE_TITLE,
|
|
126
|
+
kind: Constant::CodeActionKind::REFACTOR_REWRITE,
|
|
127
|
+
data: { range: @range, uri: @uri.to_s },
|
|
128
|
+
),
|
|
129
|
+
]
|
|
130
|
+
end
|
|
116
131
|
end
|
|
117
132
|
end
|
|
118
133
|
end
|
|
@@ -14,26 +14,47 @@ module RubyLsp
|
|
|
14
14
|
class << self
|
|
15
15
|
#: -> Interface::CodeLensOptions
|
|
16
16
|
def provider
|
|
17
|
-
Interface::CodeLensOptions.new(resolve_provider:
|
|
17
|
+
Interface::CodeLensOptions.new(resolve_provider: true)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
#: (GlobalState
|
|
22
|
-
def initialize(global_state,
|
|
23
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
21
|
+
#: (GlobalState, RubyDocument | ERBDocument, Prism::Dispatcher) -> void
|
|
22
|
+
def initialize(global_state, document, dispatcher)
|
|
23
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
24
24
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens]
|
|
25
25
|
super()
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
@document = document
|
|
28
|
+
@test_builder = ResponseBuilders::TestCollection.new #: ResponseBuilders::TestCollection
|
|
29
|
+
uri = document.uri
|
|
30
|
+
file_path = uri.full_path
|
|
31
|
+
code_lens_config = global_state.feature_configuration(:codeLens)
|
|
32
|
+
test_lenses_enabled = (!code_lens_config || code_lens_config.enabled?(:enableTestCodeLens)) &&
|
|
33
|
+
file_path && File.fnmatch?(TEST_PATH_PATTERN, file_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
34
|
+
|
|
35
|
+
if global_state.enabled_feature?(:fullTestDiscovery)
|
|
36
|
+
if test_lenses_enabled
|
|
37
|
+
Listeners::TestStyle.new(@test_builder, global_state, dispatcher, uri)
|
|
38
|
+
Listeners::SpecStyle.new(@test_builder, global_state, dispatcher, uri)
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
Listeners::CodeLens.new(@response_builder, global_state, uri, dispatcher)
|
|
42
|
+
end
|
|
27
43
|
|
|
28
44
|
Addon.addons.each do |addon|
|
|
29
45
|
addon.create_code_lens_listener(@response_builder, uri, dispatcher)
|
|
46
|
+
|
|
47
|
+
if global_state.enabled_feature?(:fullTestDiscovery) && test_lenses_enabled
|
|
48
|
+
addon.create_discover_tests_listener(@test_builder, dispatcher, uri)
|
|
49
|
+
end
|
|
30
50
|
end
|
|
31
51
|
end
|
|
32
52
|
|
|
33
53
|
# @override
|
|
34
54
|
#: -> Array[Interface::CodeLens]
|
|
35
55
|
def perform
|
|
36
|
-
@
|
|
56
|
+
@document.cache_set("rubyLsp/discoverTests", @test_builder.response)
|
|
57
|
+
@response_builder.response + @test_builder.code_lens
|
|
37
58
|
end
|
|
38
59
|
end
|
|
39
60
|
end
|
|
@@ -21,7 +21,7 @@ module RubyLsp
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] params,
|
|
24
|
+
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] params, SorbetLevel sorbet_level, Prism::Dispatcher dispatcher) -> void
|
|
25
25
|
def initialize(document, global_state, params, sorbet_level, dispatcher)
|
|
26
26
|
super()
|
|
27
27
|
@target = nil #: Prism::Node?
|
|
@@ -33,7 +33,7 @@ module RubyLsp
|
|
|
33
33
|
delegate_request_if_needed!(global_state, document, char_position)
|
|
34
34
|
|
|
35
35
|
node_context = RubyDocument.locate(
|
|
36
|
-
document.
|
|
36
|
+
document.ast,
|
|
37
37
|
char_position,
|
|
38
38
|
node_types: [
|
|
39
39
|
Prism::CallNode,
|
|
@@ -60,7 +60,7 @@ module RubyLsp
|
|
|
60
60
|
],
|
|
61
61
|
code_units_cache: document.code_units_cache,
|
|
62
62
|
)
|
|
63
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
63
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
64
64
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem]
|
|
65
65
|
|
|
66
66
|
Listeners::Completion.new(
|
|
@@ -68,10 +68,12 @@ module RubyLsp
|
|
|
68
68
|
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
@item[:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
unless @item[:kind] == Constant::CompletionItemKind::FILE
|
|
72
|
+
@item[:documentation] = Interface::MarkupContent.new(
|
|
73
|
+
kind: "markdown",
|
|
74
|
+
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
|
|
75
|
+
)
|
|
76
|
+
end
|
|
75
77
|
|
|
76
78
|
@item
|
|
77
79
|
end
|
|
@@ -84,7 +86,7 @@ module RubyLsp
|
|
|
84
86
|
content = KEYWORD_DOCS[keyword]
|
|
85
87
|
|
|
86
88
|
if content
|
|
87
|
-
|
|
89
|
+
doc_uri = URI::Generic.from_path(path: File.join(STATIC_DOCS_PATH, "#{keyword}.md"))
|
|
88
90
|
|
|
89
91
|
@item[:documentation] = Interface::MarkupContent.new(
|
|
90
92
|
kind: "markdown",
|
|
@@ -93,7 +95,7 @@ module RubyLsp
|
|
|
93
95
|
#{keyword}
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
[Read more](#{
|
|
98
|
+
[Read more](#{doc_uri})
|
|
97
99
|
|
|
98
100
|
#{content}
|
|
99
101
|
MARKDOWN
|
|
@@ -9,21 +9,18 @@ module RubyLsp
|
|
|
9
9
|
# request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
|
|
10
10
|
# definition of the symbol under the cursor.
|
|
11
11
|
class Definition < Request
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, RubyDocument::SorbetLevel sorbet_level) -> void
|
|
12
|
+
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
|
|
15
13
|
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
|
16
14
|
super()
|
|
17
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
18
|
-
|
|
19
|
-
].new #: ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)]
|
|
15
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
16
|
+
.new #: ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)]
|
|
20
17
|
@dispatcher = dispatcher
|
|
21
18
|
|
|
22
19
|
char_position, _ = document.find_index_by_position(position)
|
|
23
20
|
delegate_request_if_needed!(global_state, document, char_position)
|
|
24
21
|
|
|
25
22
|
node_context = RubyDocument.locate(
|
|
26
|
-
document.
|
|
23
|
+
document.ast,
|
|
27
24
|
char_position,
|
|
28
25
|
node_types: [
|
|
29
26
|
Prism::CallNode,
|
|
@@ -43,7 +43,7 @@ module RubyLsp
|
|
|
43
43
|
addon.create_discover_tests_listener(@response_builder, @dispatcher, @document.uri)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
@dispatcher.visit(@document.
|
|
46
|
+
@dispatcher.visit(@document.ast)
|
|
47
47
|
else
|
|
48
48
|
@global_state.synchronize do
|
|
49
49
|
RubyIndexer::DeclarationListener.new(
|
|
@@ -64,7 +64,7 @@ module RubyLsp
|
|
|
64
64
|
# Dispatch the events both for indexing the test file and discovering the tests. The order here is
|
|
65
65
|
# important because we need the index to be aware of the existing classes/modules/methods before the test
|
|
66
66
|
# listeners can do their work
|
|
67
|
-
@dispatcher.visit(@document.
|
|
67
|
+
@dispatcher.visit(@document.ast)
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -20,12 +20,12 @@ module RubyLsp
|
|
|
20
20
|
delegate_request_if_needed!(global_state, document, char_position)
|
|
21
21
|
|
|
22
22
|
node_context = RubyDocument.locate(
|
|
23
|
-
document.
|
|
23
|
+
document.ast,
|
|
24
24
|
char_position,
|
|
25
25
|
code_units_cache: document.code_units_cache,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
28
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
29
29
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight]
|
|
30
30
|
Listeners::DocumentHighlight.new(
|
|
31
31
|
@response_builder,
|
|
@@ -19,7 +19,7 @@ module RubyLsp
|
|
|
19
19
|
#: (URI::Generic uri, Array[Prism::Comment] comments, Prism::Dispatcher dispatcher) -> void
|
|
20
20
|
def initialize(uri, comments, dispatcher)
|
|
21
21
|
super()
|
|
22
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
22
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
23
23
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink]
|
|
24
24
|
Listeners::DocumentLink.new(@response_builder, uri, comments, dispatcher)
|
|
25
25
|
end
|
|
@@ -18,7 +18,7 @@ module RubyLsp
|
|
|
18
18
|
#: (Array[Prism::Comment] comments, Prism::Dispatcher dispatcher) -> void
|
|
19
19
|
def initialize(comments, dispatcher)
|
|
20
20
|
super()
|
|
21
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
21
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
22
22
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange]
|
|
23
23
|
@listener = Listeners::FoldingRanges.new(@response_builder, comments, dispatcher) #: Listeners::FoldingRanges
|
|
24
24
|
end
|
|
@@ -8,8 +8,6 @@ module RubyLsp
|
|
|
8
8
|
# that navigates to the relevant file for the current document.
|
|
9
9
|
# Currently, it supports source code file <> test file navigation.
|
|
10
10
|
class GoToRelevantFile < Request
|
|
11
|
-
extend T::Sig
|
|
12
|
-
|
|
13
11
|
TEST_KEYWORDS = ["test", "spec", "integration_test"]
|
|
14
12
|
|
|
15
13
|
TEST_PREFIX_PATTERN = /^(#{TEST_KEYWORDS.join("_|")}_)/
|
|
@@ -37,24 +35,78 @@ module RubyLsp
|
|
|
37
35
|
|
|
38
36
|
#: -> Array[String]
|
|
39
37
|
def find_relevant_paths
|
|
40
|
-
|
|
38
|
+
patterns = relevant_filename_patterns
|
|
39
|
+
|
|
40
|
+
candidate_paths = patterns.flat_map do |pattern|
|
|
41
|
+
Dir.glob(File.join(search_root, "**", pattern))
|
|
42
|
+
end
|
|
43
|
+
|
|
41
44
|
return [] if candidate_paths.empty?
|
|
42
45
|
|
|
43
|
-
find_most_similar_with_jaccard(candidate_paths).map { File.
|
|
46
|
+
find_most_similar_with_jaccard(candidate_paths).map { |path| File.expand_path(path, @workspace_path) }
|
|
44
47
|
end
|
|
45
48
|
|
|
49
|
+
# Determine the search roots based on the closest test directories.
|
|
50
|
+
# This scopes the search to reduce the number of files that need to be checked.
|
|
46
51
|
#: -> String
|
|
47
|
-
def
|
|
48
|
-
|
|
52
|
+
def search_root
|
|
53
|
+
current_path = File.join(".", @path)
|
|
54
|
+
current_dir = File.dirname(current_path)
|
|
55
|
+
while current_dir != "."
|
|
56
|
+
dir_basename = File.basename(current_dir)
|
|
57
|
+
|
|
58
|
+
# If current directory is a test directory, return its parent as search root
|
|
59
|
+
if TEST_KEYWORDS.include?(dir_basename)
|
|
60
|
+
return File.dirname(current_dir)
|
|
61
|
+
end
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
# Search the test directories by walking up the directory tree
|
|
64
|
+
begin
|
|
65
|
+
contains_test_dir = Dir
|
|
66
|
+
.entries(current_dir)
|
|
67
|
+
.filter { |entry| TEST_KEYWORDS.include?(entry) }
|
|
68
|
+
.any? { |entry| File.directory?(File.join(current_dir, entry)) }
|
|
69
|
+
|
|
70
|
+
return current_dir if contains_test_dir
|
|
71
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
72
|
+
# Skip directories we can't read
|
|
55
73
|
end
|
|
56
74
|
|
|
57
|
-
|
|
75
|
+
# Move up one level
|
|
76
|
+
parent_dir = File.dirname(current_dir)
|
|
77
|
+
current_dir = parent_dir
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
"."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#: -> Array[String]
|
|
84
|
+
def relevant_filename_patterns
|
|
85
|
+
extension = File.extname(@path)
|
|
86
|
+
input_basename = File.basename(@path, extension)
|
|
87
|
+
|
|
88
|
+
if input_basename.match?(TEST_PATTERN)
|
|
89
|
+
# Test file -> find implementation
|
|
90
|
+
base = input_basename.gsub(TEST_PATTERN, "")
|
|
91
|
+
parent_dir = File.basename(File.dirname(@path))
|
|
92
|
+
|
|
93
|
+
# If test file is in a directory matching the implementation name
|
|
94
|
+
# (e.g., go_to_relevant_file/test_go_to_relevant_file_a.rb)
|
|
95
|
+
# return patterns for both the base file name and the parent directory name
|
|
96
|
+
if base.include?(parent_dir) && base != parent_dir
|
|
97
|
+
["#{base}#{extension}", "#{parent_dir}#{extension}"]
|
|
98
|
+
else
|
|
99
|
+
["#{base}#{extension}"]
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
# Implementation file -> find tests (including in matching directory)
|
|
103
|
+
[
|
|
104
|
+
"{#{TEST_PREFIX_GLOB}}#{input_basename}#{extension}",
|
|
105
|
+
"#{input_basename}{#{TEST_SUFFIX_GLOB}}#{extension}",
|
|
106
|
+
"#{input_basename}/{#{TEST_PREFIX_GLOB}}*#{extension}",
|
|
107
|
+
"#{input_basename}/*{#{TEST_SUFFIX_GLOB}}#{extension}",
|
|
108
|
+
]
|
|
109
|
+
end
|
|
58
110
|
end
|
|
59
111
|
|
|
60
112
|
# Using the Jaccard algorithm to determine the similarity between the
|
|
@@ -7,9 +7,8 @@ module RubyLsp
|
|
|
7
7
|
module Requests
|
|
8
8
|
# The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
|
|
9
9
|
# displays the documentation for the symbol currently under the cursor.
|
|
10
|
+
#: [ResponseType = Interface::Hover?]
|
|
10
11
|
class Hover < Request
|
|
11
|
-
extend T::Generic
|
|
12
|
-
|
|
13
12
|
class << self
|
|
14
13
|
#: -> Interface::HoverOptions
|
|
15
14
|
def provider
|
|
@@ -17,9 +16,7 @@ module RubyLsp
|
|
|
17
16
|
end
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, RubyDocument::SorbetLevel sorbet_level) -> void
|
|
19
|
+
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
|
|
23
20
|
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
|
24
21
|
super()
|
|
25
22
|
|
|
@@ -27,7 +24,7 @@ module RubyLsp
|
|
|
27
24
|
delegate_request_if_needed!(global_state, document, char_position)
|
|
28
25
|
|
|
29
26
|
node_context = RubyDocument.locate(
|
|
30
|
-
document.
|
|
27
|
+
document.ast,
|
|
31
28
|
char_position,
|
|
32
29
|
node_types: Listeners::Hover::ALLOWED_TARGETS,
|
|
33
30
|
code_units_cache: document.code_units_cache,
|
|
@@ -16,13 +16,13 @@ module RubyLsp
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
#: ((RubyDocument | ERBDocument)
|
|
20
|
-
def initialize(
|
|
19
|
+
#: (GlobalState, (RubyDocument | ERBDocument), Prism::Dispatcher) -> void
|
|
20
|
+
def initialize(global_state, document, dispatcher)
|
|
21
21
|
super()
|
|
22
22
|
|
|
23
|
-
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
23
|
+
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
|
24
24
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint]
|
|
25
|
-
Listeners::InlayHints.new(@response_builder,
|
|
25
|
+
Listeners::InlayHints.new(global_state, @response_builder, dispatcher)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# @override
|
|
@@ -162,7 +162,7 @@ module RubyLsp
|
|
|
162
162
|
|
|
163
163
|
#: (Integer line, Integer character) -> void
|
|
164
164
|
def move_cursor_to(line, character)
|
|
165
|
-
return unless /Visual Studio Code|Cursor|VSCodium/.match?(@client_name)
|
|
165
|
+
return unless /Visual Studio Code|Cursor|VSCodium|Windsurf/.match?(@client_name)
|
|
166
166
|
|
|
167
167
|
position = Interface::Position.new(
|
|
168
168
|
line: line,
|