ruby-lsp 0.22.1 → 0.23.10
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 +12 -11
- data/exe/ruby-lsp-check +5 -5
- data/exe/ruby-lsp-launcher +41 -15
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +191 -100
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +174 -61
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +12 -0
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +82 -61
- data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
- data/lib/ruby_indexer/ruby_indexer.rb +2 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +30 -6
- data/lib/ruby_indexer/test/configuration_test.rb +116 -51
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/index_test.rb +143 -44
- data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
- data/lib/ruby_indexer/test/method_test.rb +86 -8
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_indexer/test/reference_finder_test.rb +90 -2
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_indexer/test/uri_test.rb +72 -0
- data/lib/ruby_lsp/addon.rb +9 -0
- data/lib/ruby_lsp/base_server.rb +17 -18
- data/lib/ruby_lsp/client_capabilities.rb +7 -1
- data/lib/ruby_lsp/document.rb +72 -10
- data/lib/ruby_lsp/erb_document.rb +5 -3
- data/lib/ruby_lsp/global_state.rb +42 -3
- data/lib/ruby_lsp/internal.rb +3 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +9 -5
- data/lib/ruby_lsp/listeners/completion.rb +78 -6
- data/lib/ruby_lsp/listeners/definition.rb +80 -19
- data/lib/ruby_lsp/listeners/document_highlight.rb +3 -2
- data/lib/ruby_lsp/listeners/document_link.rb +21 -3
- data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
- data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/listeners/hover.rb +59 -2
- data/lib/ruby_lsp/load_sorbet.rb +3 -3
- data/lib/ruby_lsp/rbs_document.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +90 -6
- data/lib/ruby_lsp/requests/code_actions.rb +57 -1
- data/lib/ruby_lsp/requests/completion.rb +8 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +7 -1
- data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/folding_ranges.rb +2 -6
- data/lib/ruby_lsp/requests/formatting.rb +2 -6
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -2
- data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +29 -2
- data/lib/ruby_lsp/requests/rename.rb +17 -7
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +2 -9
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +3 -3
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -13
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -3
- data/lib/ruby_lsp/ruby_document.rb +80 -6
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +205 -61
- data/lib/ruby_lsp/setup_bundler.rb +50 -43
- data/lib/ruby_lsp/store.rb +7 -7
- data/lib/ruby_lsp/test_helper.rb +45 -11
- data/lib/ruby_lsp/type_inferrer.rb +60 -31
- data/lib/ruby_lsp/utils.rb +63 -3
- metadata +8 -8
- data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
@@ -97,6 +97,12 @@ module RubyLsp
|
|
97
97
|
:on_instance_variable_operator_write_node_enter,
|
98
98
|
:on_instance_variable_or_write_node_enter,
|
99
99
|
:on_instance_variable_target_node_enter,
|
100
|
+
:on_class_variable_and_write_node_enter,
|
101
|
+
:on_class_variable_operator_write_node_enter,
|
102
|
+
:on_class_variable_or_write_node_enter,
|
103
|
+
:on_class_variable_read_node_enter,
|
104
|
+
:on_class_variable_target_node_enter,
|
105
|
+
:on_class_variable_write_node_enter,
|
100
106
|
)
|
101
107
|
end
|
102
108
|
|
@@ -107,16 +113,17 @@ module RubyLsp
|
|
107
113
|
# no sigil, Sorbet will still provide completion for constants
|
108
114
|
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
109
115
|
|
110
|
-
name = constant_name(node)
|
116
|
+
name = RubyIndexer::Index.constant_name(node)
|
111
117
|
return if name.nil?
|
112
118
|
|
119
|
+
range = range_from_location(node.location)
|
113
120
|
candidates = @index.constant_completion_candidates(name, @node_context.nesting)
|
114
121
|
candidates.each do |entries|
|
115
122
|
complete_name = T.must(entries.first).name
|
116
123
|
@response_builder << build_entry_completion(
|
117
124
|
complete_name,
|
118
125
|
name,
|
119
|
-
|
126
|
+
range,
|
120
127
|
entries,
|
121
128
|
top_level?(complete_name),
|
122
129
|
)
|
@@ -155,7 +162,7 @@ module RubyLsp
|
|
155
162
|
if (receiver.is_a?(Prism::ConstantReadNode) || receiver.is_a?(Prism::ConstantPathNode)) &&
|
156
163
|
node.call_operator == "::"
|
157
164
|
|
158
|
-
name = constant_name(receiver)
|
165
|
+
name = RubyIndexer::Index.constant_name(receiver)
|
159
166
|
|
160
167
|
if name
|
161
168
|
start_loc = node.location
|
@@ -246,6 +253,36 @@ module RubyLsp
|
|
246
253
|
handle_instance_variable_completion(node.name.to_s, node.location)
|
247
254
|
end
|
248
255
|
|
256
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
257
|
+
def on_class_variable_and_write_node_enter(node)
|
258
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
259
|
+
end
|
260
|
+
|
261
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
262
|
+
def on_class_variable_operator_write_node_enter(node)
|
263
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
264
|
+
end
|
265
|
+
|
266
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
267
|
+
def on_class_variable_or_write_node_enter(node)
|
268
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
269
|
+
end
|
270
|
+
|
271
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
272
|
+
def on_class_variable_target_node_enter(node)
|
273
|
+
handle_class_variable_completion(node.name.to_s, node.location)
|
274
|
+
end
|
275
|
+
|
276
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
277
|
+
def on_class_variable_read_node_enter(node)
|
278
|
+
handle_class_variable_completion(node.name.to_s, node.location)
|
279
|
+
end
|
280
|
+
|
281
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
282
|
+
def on_class_variable_write_node_enter(node)
|
283
|
+
handle_class_variable_completion(node.name.to_s, node.name_loc)
|
284
|
+
end
|
285
|
+
|
249
286
|
private
|
250
287
|
|
251
288
|
sig { params(name: String, range: Interface::Range).void }
|
@@ -326,6 +363,37 @@ module RubyLsp
|
|
326
363
|
end
|
327
364
|
end
|
328
365
|
|
366
|
+
sig { params(name: String, location: Prism::Location).void }
|
367
|
+
def handle_class_variable_completion(name, location)
|
368
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
369
|
+
return unless type
|
370
|
+
|
371
|
+
range = range_from_location(location)
|
372
|
+
|
373
|
+
@index.class_variable_completion_candidates(name, type.name).each do |entry|
|
374
|
+
variable_name = entry.name
|
375
|
+
|
376
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
377
|
+
description: entry.file_name,
|
378
|
+
)
|
379
|
+
|
380
|
+
@response_builder << Interface::CompletionItem.new(
|
381
|
+
label: variable_name,
|
382
|
+
label_details: label_details,
|
383
|
+
text_edit: Interface::TextEdit.new(
|
384
|
+
range: range,
|
385
|
+
new_text: variable_name,
|
386
|
+
),
|
387
|
+
kind: Constant::CompletionItemKind::FIELD,
|
388
|
+
data: {
|
389
|
+
owner_name: entry.owner&.name,
|
390
|
+
},
|
391
|
+
)
|
392
|
+
end
|
393
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
394
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
395
|
+
end
|
396
|
+
|
329
397
|
sig { params(name: String, location: Prism::Location).void }
|
330
398
|
def handle_instance_variable_completion(name, location)
|
331
399
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -335,6 +403,7 @@ module RubyLsp
|
|
335
403
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
336
404
|
return unless type
|
337
405
|
|
406
|
+
range = range_from_location(location)
|
338
407
|
@index.instance_variable_completion_candidates(name, type.name).each do |entry|
|
339
408
|
variable_name = entry.name
|
340
409
|
|
@@ -346,7 +415,7 @@ module RubyLsp
|
|
346
415
|
label: variable_name,
|
347
416
|
label_details: label_details,
|
348
417
|
text_edit: Interface::TextEdit.new(
|
349
|
-
range:
|
418
|
+
range: range,
|
350
419
|
new_text: variable_name,
|
351
420
|
),
|
352
421
|
kind: Constant::CompletionItemKind::FIELD,
|
@@ -368,9 +437,9 @@ module RubyLsp
|
|
368
437
|
|
369
438
|
return unless path_node_to_complete.is_a?(Prism::StringNode)
|
370
439
|
|
371
|
-
|
440
|
+
matched_uris = @index.search_require_paths(path_node_to_complete.content)
|
372
441
|
|
373
|
-
|
442
|
+
matched_uris.map!(&:require_path).sort!.each do |path|
|
374
443
|
@response_builder << build_completion(T.must(path), path_node_to_complete)
|
375
444
|
end
|
376
445
|
end
|
@@ -402,6 +471,9 @@ module RubyLsp
|
|
402
471
|
path_node_to_complete,
|
403
472
|
)
|
404
473
|
end
|
474
|
+
rescue Errno::EPERM
|
475
|
+
# If the user writes a relative require pointing to a path that the editor has no permissions to read, then glob
|
476
|
+
# might fail with EPERM
|
405
477
|
end
|
406
478
|
|
407
479
|
sig { params(node: Prism::CallNode, name: String).void }
|
@@ -55,6 +55,12 @@ module RubyLsp
|
|
55
55
|
:on_symbol_node_enter,
|
56
56
|
:on_super_node_enter,
|
57
57
|
:on_forwarding_super_node_enter,
|
58
|
+
:on_class_variable_and_write_node_enter,
|
59
|
+
:on_class_variable_operator_write_node_enter,
|
60
|
+
:on_class_variable_or_write_node_enter,
|
61
|
+
:on_class_variable_read_node_enter,
|
62
|
+
:on_class_variable_target_node_enter,
|
63
|
+
:on_class_variable_write_node_enter,
|
58
64
|
)
|
59
65
|
end
|
60
66
|
|
@@ -112,7 +118,7 @@ module RubyLsp
|
|
112
118
|
|
113
119
|
sig { params(node: Prism::ConstantPathNode).void }
|
114
120
|
def on_constant_path_node_enter(node)
|
115
|
-
name = constant_name(node)
|
121
|
+
name = RubyIndexer::Index.constant_name(node)
|
116
122
|
return if name.nil?
|
117
123
|
|
118
124
|
find_in_index(name)
|
@@ -120,7 +126,7 @@ module RubyLsp
|
|
120
126
|
|
121
127
|
sig { params(node: Prism::ConstantReadNode).void }
|
122
128
|
def on_constant_read_node_enter(node)
|
123
|
-
name = constant_name(node)
|
129
|
+
name = RubyIndexer::Index.constant_name(node)
|
124
130
|
return if name.nil?
|
125
131
|
|
126
132
|
find_in_index(name)
|
@@ -196,6 +202,36 @@ module RubyLsp
|
|
196
202
|
handle_super_node_definition
|
197
203
|
end
|
198
204
|
|
205
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
206
|
+
def on_class_variable_and_write_node_enter(node)
|
207
|
+
handle_class_variable_definition(node.name.to_s)
|
208
|
+
end
|
209
|
+
|
210
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
211
|
+
def on_class_variable_operator_write_node_enter(node)
|
212
|
+
handle_class_variable_definition(node.name.to_s)
|
213
|
+
end
|
214
|
+
|
215
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
216
|
+
def on_class_variable_or_write_node_enter(node)
|
217
|
+
handle_class_variable_definition(node.name.to_s)
|
218
|
+
end
|
219
|
+
|
220
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
221
|
+
def on_class_variable_target_node_enter(node)
|
222
|
+
handle_class_variable_definition(node.name.to_s)
|
223
|
+
end
|
224
|
+
|
225
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
226
|
+
def on_class_variable_read_node_enter(node)
|
227
|
+
handle_class_variable_definition(node.name.to_s)
|
228
|
+
end
|
229
|
+
|
230
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
231
|
+
def on_class_variable_write_node_enter(node)
|
232
|
+
handle_class_variable_definition(node.name.to_s)
|
233
|
+
end
|
234
|
+
|
199
235
|
private
|
200
236
|
|
201
237
|
sig { void }
|
@@ -223,7 +259,7 @@ module RubyLsp
|
|
223
259
|
location = entry.location
|
224
260
|
|
225
261
|
@response_builder << Interface::Location.new(
|
226
|
-
uri:
|
262
|
+
uri: entry.uri.to_s,
|
227
263
|
range: Interface::Range.new(
|
228
264
|
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
229
265
|
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
@@ -232,6 +268,24 @@ module RubyLsp
|
|
232
268
|
end
|
233
269
|
end
|
234
270
|
|
271
|
+
sig { params(name: String).void }
|
272
|
+
def handle_class_variable_definition(name)
|
273
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
274
|
+
return unless type
|
275
|
+
|
276
|
+
entries = @index.resolve_class_variable(name, type.name)
|
277
|
+
return unless entries
|
278
|
+
|
279
|
+
entries.each do |entry|
|
280
|
+
@response_builder << Interface::Location.new(
|
281
|
+
uri: entry.uri.to_s,
|
282
|
+
range: range_from_location(entry.location),
|
283
|
+
)
|
284
|
+
end
|
285
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
286
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
287
|
+
end
|
288
|
+
|
235
289
|
sig { params(name: String).void }
|
236
290
|
def handle_instance_variable_definition(name)
|
237
291
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -248,7 +302,7 @@ module RubyLsp
|
|
248
302
|
location = entry.location
|
249
303
|
|
250
304
|
@response_builder << Interface::Location.new(
|
251
|
-
uri:
|
305
|
+
uri: entry.uri.to_s,
|
252
306
|
range: Interface::Range.new(
|
253
307
|
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
254
308
|
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
@@ -275,11 +329,12 @@ module RubyLsp
|
|
275
329
|
return unless methods
|
276
330
|
|
277
331
|
methods.each do |target_method|
|
278
|
-
|
279
|
-
|
332
|
+
uri = target_method.uri
|
333
|
+
full_path = uri.full_path
|
334
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && (!full_path || not_in_dependencies?(full_path))
|
280
335
|
|
281
336
|
@response_builder << Interface::LocationLink.new(
|
282
|
-
target_uri:
|
337
|
+
target_uri: uri.to_s,
|
283
338
|
target_range: range_from_location(target_method.location),
|
284
339
|
target_selection_range: range_from_location(target_method.name_location),
|
285
340
|
)
|
@@ -290,20 +345,22 @@ module RubyLsp
|
|
290
345
|
def handle_require_definition(node, message)
|
291
346
|
case message
|
292
347
|
when :require
|
293
|
-
entry = @index.search_require_paths(node.content).find do |
|
294
|
-
|
348
|
+
entry = @index.search_require_paths(node.content).find do |uri|
|
349
|
+
uri.require_path == node.content
|
295
350
|
end
|
296
351
|
|
297
352
|
if entry
|
298
353
|
candidate = entry.full_path
|
299
354
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
355
|
+
if candidate
|
356
|
+
@response_builder << Interface::Location.new(
|
357
|
+
uri: URI::Generic.from_path(path: candidate).to_s,
|
358
|
+
range: Interface::Range.new(
|
359
|
+
start: Interface::Position.new(line: 0, character: 0),
|
360
|
+
end: Interface::Position.new(line: 0, character: 0),
|
361
|
+
),
|
362
|
+
)
|
363
|
+
end
|
307
364
|
end
|
308
365
|
when :require_relative
|
309
366
|
required_file = "#{node.content}.rb"
|
@@ -346,11 +403,15 @@ module RubyLsp
|
|
346
403
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
347
404
|
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
348
405
|
# ignore
|
349
|
-
|
350
|
-
|
406
|
+
uri = entry.uri
|
407
|
+
full_path = uri.full_path
|
408
|
+
|
409
|
+
if @sorbet_level != RubyDocument::SorbetLevel::Ignore && (!full_path || not_in_dependencies?(full_path))
|
410
|
+
next
|
411
|
+
end
|
351
412
|
|
352
413
|
@response_builder << Interface::LocationLink.new(
|
353
|
-
target_uri:
|
414
|
+
target_uri: uri.to_s,
|
354
415
|
target_range: range_from_location(entry.location),
|
355
416
|
target_selection_range: range_from_location(entry.name_location),
|
356
417
|
)
|
@@ -120,7 +120,7 @@ module RubyLsp
|
|
120
120
|
[target, node_value(target)]
|
121
121
|
when Prism::ModuleNode, Prism::ClassNode, Prism::SingletonClassNode, Prism::DefNode, Prism::CaseNode,
|
122
122
|
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode
|
123
|
-
target
|
123
|
+
[target, nil]
|
124
124
|
end
|
125
125
|
|
126
126
|
@target = T.let(highlight_target, T.nilable(Prism::Node))
|
@@ -620,7 +620,8 @@ module RubyLsp
|
|
620
620
|
|
621
621
|
sig { params(keyword_loc: T.nilable(Prism::Location), end_loc: T.nilable(Prism::Location)).void }
|
622
622
|
def add_matching_end_highlights(keyword_loc, end_loc)
|
623
|
-
return unless keyword_loc && end_loc
|
623
|
+
return unless keyword_loc && end_loc
|
624
|
+
return unless end_loc.length.positive?
|
624
625
|
return unless covers_target_position?(keyword_loc) || covers_target_position?(end_loc)
|
625
626
|
|
626
627
|
add_highlight(Constant::DocumentHighlightKind::TEXT, keyword_loc)
|
@@ -124,11 +124,26 @@ module RubyLsp
|
|
124
124
|
match = comment.location.slice.match(%r{source://.*#\d+$})
|
125
125
|
return unless match
|
126
126
|
|
127
|
-
uri = T.cast(
|
127
|
+
uri = T.cast(
|
128
|
+
begin
|
129
|
+
URI(T.must(match[0]))
|
130
|
+
rescue URI::Error
|
131
|
+
nil
|
132
|
+
end,
|
133
|
+
T.nilable(URI::Source),
|
134
|
+
)
|
135
|
+
return unless uri
|
136
|
+
|
128
137
|
gem_version = resolve_version(uri)
|
129
138
|
return if gem_version.nil?
|
130
139
|
|
131
|
-
|
140
|
+
path = uri.path
|
141
|
+
return unless path
|
142
|
+
|
143
|
+
gem_name = uri.gem_name
|
144
|
+
return unless gem_name
|
145
|
+
|
146
|
+
file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
|
132
147
|
return if file_path.nil?
|
133
148
|
|
134
149
|
@response_builder << Interface::DocumentLink.new(
|
@@ -149,7 +164,10 @@ module RubyLsp
|
|
149
164
|
|
150
165
|
return @gem_version unless @gem_version.nil? || @gem_version.empty?
|
151
166
|
|
152
|
-
|
167
|
+
gem_name = uri.gem_name
|
168
|
+
return unless gem_name
|
169
|
+
|
170
|
+
GEM_TO_VERSION_MAP[gem_name]
|
153
171
|
end
|
154
172
|
end
|
155
173
|
end
|
@@ -41,6 +41,7 @@ module RubyLsp
|
|
41
41
|
:on_module_node_enter,
|
42
42
|
:on_module_node_leave,
|
43
43
|
:on_instance_variable_write_node_enter,
|
44
|
+
:on_instance_variable_target_node_enter,
|
44
45
|
:on_instance_variable_operator_write_node_enter,
|
45
46
|
:on_instance_variable_or_write_node_enter,
|
46
47
|
:on_instance_variable_and_write_node_enter,
|
@@ -272,6 +273,16 @@ module RubyLsp
|
|
272
273
|
)
|
273
274
|
end
|
274
275
|
|
276
|
+
sig { params(node: Prism::InstanceVariableTargetNode).void }
|
277
|
+
def on_instance_variable_target_node_enter(node)
|
278
|
+
create_document_symbol(
|
279
|
+
name: node.name.to_s,
|
280
|
+
kind: Constant::SymbolKind::FIELD,
|
281
|
+
range_location: node.location,
|
282
|
+
selection_range_location: node.location,
|
283
|
+
)
|
284
|
+
end
|
285
|
+
|
275
286
|
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
276
287
|
def on_instance_variable_operator_write_node_enter(node)
|
277
288
|
create_document_symbol(
|
@@ -329,7 +340,7 @@ module RubyLsp
|
|
329
340
|
).returns(Interface::DocumentSymbol)
|
330
341
|
end
|
331
342
|
def create_document_symbol(name:, kind:, range_location:, selection_range_location:)
|
332
|
-
name = "<blank>" if name.empty?
|
343
|
+
name = "<blank>" if name.strip.empty?
|
333
344
|
symbol = Interface::DocumentSymbol.new(
|
334
345
|
name: name,
|
335
346
|
kind: kind,
|
@@ -195,7 +195,7 @@ module RubyLsp
|
|
195
195
|
def push_comment_ranges
|
196
196
|
# Group comments that are on consecutive lines and then push ranges for each group that has at least 2 comments
|
197
197
|
@comments.chunk_while do |this, other|
|
198
|
-
this.location.end_line + 1 == other.location.start_line
|
198
|
+
this.location.end_line + 1 == other.location.start_line && !this.trailing? && !other.trailing?
|
199
199
|
end.each do |chunk|
|
200
200
|
next if chunk.length == 1
|
201
201
|
|
@@ -31,6 +31,12 @@ module RubyLsp
|
|
31
31
|
Prism::SuperNode,
|
32
32
|
Prism::ForwardingSuperNode,
|
33
33
|
Prism::YieldNode,
|
34
|
+
Prism::ClassVariableAndWriteNode,
|
35
|
+
Prism::ClassVariableOperatorWriteNode,
|
36
|
+
Prism::ClassVariableOrWriteNode,
|
37
|
+
Prism::ClassVariableReadNode,
|
38
|
+
Prism::ClassVariableTargetNode,
|
39
|
+
Prism::ClassVariableWriteNode,
|
34
40
|
],
|
35
41
|
T::Array[T.class_of(Prism::Node)],
|
36
42
|
)
|
@@ -85,6 +91,12 @@ module RubyLsp
|
|
85
91
|
:on_string_node_enter,
|
86
92
|
:on_interpolated_string_node_enter,
|
87
93
|
:on_yield_node_enter,
|
94
|
+
:on_class_variable_and_write_node_enter,
|
95
|
+
:on_class_variable_operator_write_node_enter,
|
96
|
+
:on_class_variable_or_write_node_enter,
|
97
|
+
:on_class_variable_read_node_enter,
|
98
|
+
:on_class_variable_target_node_enter,
|
99
|
+
:on_class_variable_write_node_enter,
|
88
100
|
)
|
89
101
|
end
|
90
102
|
|
@@ -102,7 +114,7 @@ module RubyLsp
|
|
102
114
|
def on_constant_read_node_enter(node)
|
103
115
|
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
104
116
|
|
105
|
-
name = constant_name(node)
|
117
|
+
name = RubyIndexer::Index.constant_name(node)
|
106
118
|
return if name.nil?
|
107
119
|
|
108
120
|
generate_hover(name, node.location)
|
@@ -119,7 +131,7 @@ module RubyLsp
|
|
119
131
|
def on_constant_path_node_enter(node)
|
120
132
|
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
121
133
|
|
122
|
-
name = constant_name(node)
|
134
|
+
name = RubyIndexer::Index.constant_name(node)
|
123
135
|
return if name.nil?
|
124
136
|
|
125
137
|
generate_hover(name, node.location)
|
@@ -215,6 +227,36 @@ module RubyLsp
|
|
215
227
|
handle_keyword_documentation(node.keyword)
|
216
228
|
end
|
217
229
|
|
230
|
+
sig { params(node: Prism::ClassVariableAndWriteNode).void }
|
231
|
+
def on_class_variable_and_write_node_enter(node)
|
232
|
+
handle_class_variable_hover(node.name.to_s)
|
233
|
+
end
|
234
|
+
|
235
|
+
sig { params(node: Prism::ClassVariableOperatorWriteNode).void }
|
236
|
+
def on_class_variable_operator_write_node_enter(node)
|
237
|
+
handle_class_variable_hover(node.name.to_s)
|
238
|
+
end
|
239
|
+
|
240
|
+
sig { params(node: Prism::ClassVariableOrWriteNode).void }
|
241
|
+
def on_class_variable_or_write_node_enter(node)
|
242
|
+
handle_class_variable_hover(node.name.to_s)
|
243
|
+
end
|
244
|
+
|
245
|
+
sig { params(node: Prism::ClassVariableTargetNode).void }
|
246
|
+
def on_class_variable_target_node_enter(node)
|
247
|
+
handle_class_variable_hover(node.name.to_s)
|
248
|
+
end
|
249
|
+
|
250
|
+
sig { params(node: Prism::ClassVariableReadNode).void }
|
251
|
+
def on_class_variable_read_node_enter(node)
|
252
|
+
handle_class_variable_hover(node.name.to_s)
|
253
|
+
end
|
254
|
+
|
255
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
256
|
+
def on_class_variable_write_node_enter(node)
|
257
|
+
handle_class_variable_hover(node.name.to_s)
|
258
|
+
end
|
259
|
+
|
218
260
|
private
|
219
261
|
|
220
262
|
sig { params(node: T.any(Prism::InterpolatedStringNode, Prism::StringNode)).void }
|
@@ -317,6 +359,21 @@ module RubyLsp
|
|
317
359
|
end
|
318
360
|
end
|
319
361
|
|
362
|
+
sig { params(name: String).void }
|
363
|
+
def handle_class_variable_hover(name)
|
364
|
+
type = @type_inferrer.infer_receiver_type(@node_context)
|
365
|
+
return unless type
|
366
|
+
|
367
|
+
entries = @index.resolve_class_variable(name, type.name)
|
368
|
+
return unless entries
|
369
|
+
|
370
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
371
|
+
@response_builder.push(content, category: category)
|
372
|
+
end
|
373
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
374
|
+
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
375
|
+
end
|
376
|
+
|
320
377
|
sig { params(name: String, location: Prism::Location).void }
|
321
378
|
def generate_hover(name, location)
|
322
379
|
entries = @index.resolve(name, @node_context.nesting)
|
data/lib/ruby_lsp/load_sorbet.rb
CHANGED
@@ -6,11 +6,11 @@ require "sorbet-runtime"
|
|
6
6
|
begin
|
7
7
|
T::Configuration.default_checked_level = :never
|
8
8
|
# Suppresses call validation errors
|
9
|
-
T::Configuration.call_validation_error_handler = ->(*) {}
|
9
|
+
T::Configuration.call_validation_error_handler = ->(*arg) {}
|
10
10
|
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
11
|
-
T::Configuration.inline_type_error_handler = ->(*) {}
|
11
|
+
T::Configuration.inline_type_error_handler = ->(*arg) {}
|
12
12
|
# Suppresses errors caused by incorrect parameter ordering
|
13
|
-
T::Configuration.sig_validation_error_handler = ->(*) {}
|
13
|
+
T::Configuration.sig_validation_error_handler = ->(*arg) {}
|
14
14
|
rescue
|
15
15
|
# Need this rescue so that if another gem has
|
16
16
|
# already set the checked level by the time we
|
@@ -8,8 +8,8 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
|
10
10
|
|
11
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
12
|
-
def initialize(source:, version:, uri:,
|
11
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
12
|
+
def initialize(source:, version:, uri:, global_state:)
|
13
13
|
@syntax_error = T.let(false, T::Boolean)
|
14
14
|
super
|
15
15
|
end
|
@@ -42,6 +42,10 @@ module RubyLsp
|
|
42
42
|
refactor_method
|
43
43
|
when CodeActions::TOGGLE_BLOCK_STYLE_TITLE
|
44
44
|
switch_block_style
|
45
|
+
when CodeActions::CREATE_ATTRIBUTE_READER,
|
46
|
+
CodeActions::CREATE_ATTRIBUTE_WRITER,
|
47
|
+
CodeActions::CREATE_ATTRIBUTE_ACCESSOR
|
48
|
+
create_attribute_accessor
|
45
49
|
else
|
46
50
|
Error::UnknownCodeAction
|
47
51
|
end
|
@@ -92,9 +96,7 @@ module RubyLsp
|
|
92
96
|
source_range = @code_action.dig(:data, :range)
|
93
97
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
94
98
|
|
95
|
-
|
96
|
-
start_index = scanner.find_char_position(source_range[:start])
|
97
|
-
end_index = scanner.find_char_position(source_range[:end])
|
99
|
+
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
98
100
|
extracted_source = T.must(@document.source[start_index...end_index])
|
99
101
|
|
100
102
|
# Find the closest statements node, so that we place the refactor in a valid position
|
@@ -192,9 +194,7 @@ module RubyLsp
|
|
192
194
|
source_range = @code_action.dig(:data, :range)
|
193
195
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
194
196
|
|
195
|
-
|
196
|
-
start_index = scanner.find_char_position(source_range[:start])
|
197
|
-
end_index = scanner.find_char_position(source_range[:end])
|
197
|
+
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
198
198
|
extracted_source = T.must(@document.source[start_index...end_index])
|
199
199
|
|
200
200
|
# Find the closest method declaration node, so that we place the refactor in a valid position
|
@@ -329,6 +329,90 @@ module RubyLsp
|
|
329
329
|
|
330
330
|
indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
|
331
331
|
end
|
332
|
+
|
333
|
+
sig { returns(T.any(Interface::CodeAction, Error)) }
|
334
|
+
def create_attribute_accessor
|
335
|
+
source_range = @code_action.dig(:data, :range)
|
336
|
+
|
337
|
+
node = if source_range[:start] != source_range[:end]
|
338
|
+
@document.locate_first_within_range(
|
339
|
+
@code_action.dig(:data, :range),
|
340
|
+
node_types: CodeActions::INSTANCE_VARIABLE_NODES,
|
341
|
+
)
|
342
|
+
end
|
343
|
+
|
344
|
+
if node.nil?
|
345
|
+
node_context = @document.locate_node(
|
346
|
+
source_range[:start],
|
347
|
+
node_types: CodeActions::INSTANCE_VARIABLE_NODES,
|
348
|
+
)
|
349
|
+
node = node_context.node
|
350
|
+
|
351
|
+
return Error::EmptySelection unless CodeActions::INSTANCE_VARIABLE_NODES.include?(node.class)
|
352
|
+
end
|
353
|
+
|
354
|
+
node = T.cast(
|
355
|
+
node,
|
356
|
+
T.any(
|
357
|
+
Prism::InstanceVariableAndWriteNode,
|
358
|
+
Prism::InstanceVariableOperatorWriteNode,
|
359
|
+
Prism::InstanceVariableOrWriteNode,
|
360
|
+
Prism::InstanceVariableReadNode,
|
361
|
+
Prism::InstanceVariableTargetNode,
|
362
|
+
Prism::InstanceVariableWriteNode,
|
363
|
+
),
|
364
|
+
)
|
365
|
+
|
366
|
+
node_context = @document.locate_node(
|
367
|
+
{
|
368
|
+
line: node.location.start_line,
|
369
|
+
character: node.location.start_character_column,
|
370
|
+
},
|
371
|
+
node_types: [
|
372
|
+
Prism::ClassNode,
|
373
|
+
Prism::ModuleNode,
|
374
|
+
Prism::SingletonClassNode,
|
375
|
+
],
|
376
|
+
)
|
377
|
+
closest_node = node_context.node
|
378
|
+
return Error::InvalidTargetRange if closest_node.nil?
|
379
|
+
|
380
|
+
attribute_name = node.name[1..]
|
381
|
+
indentation = " " * (closest_node.location.start_column + 2)
|
382
|
+
attribute_accessor_source = T.must(
|
383
|
+
case @code_action[:title]
|
384
|
+
when CodeActions::CREATE_ATTRIBUTE_READER
|
385
|
+
"#{indentation}attr_reader :#{attribute_name}\n\n"
|
386
|
+
when CodeActions::CREATE_ATTRIBUTE_WRITER
|
387
|
+
"#{indentation}attr_writer :#{attribute_name}\n\n"
|
388
|
+
when CodeActions::CREATE_ATTRIBUTE_ACCESSOR
|
389
|
+
"#{indentation}attr_accessor :#{attribute_name}\n\n"
|
390
|
+
end,
|
391
|
+
)
|
392
|
+
|
393
|
+
target_start_line = closest_node.location.start_line
|
394
|
+
target_range = {
|
395
|
+
start: { line: target_start_line, character: 0 },
|
396
|
+
end: { line: target_start_line, character: 0 },
|
397
|
+
}
|
398
|
+
|
399
|
+
Interface::CodeAction.new(
|
400
|
+
title: @code_action[:title],
|
401
|
+
edit: Interface::WorkspaceEdit.new(
|
402
|
+
document_changes: [
|
403
|
+
Interface::TextDocumentEdit.new(
|
404
|
+
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
405
|
+
uri: @code_action.dig(:data, :uri),
|
406
|
+
version: nil,
|
407
|
+
),
|
408
|
+
edits: [
|
409
|
+
create_text_edit(target_range, attribute_accessor_source),
|
410
|
+
],
|
411
|
+
),
|
412
|
+
],
|
413
|
+
),
|
414
|
+
)
|
415
|
+
end
|
332
416
|
end
|
333
417
|
end
|
334
418
|
end
|