ruby-lsp 0.22.0 → 0.23.0

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +10 -9
  4. data/exe/ruby-lsp-check +5 -5
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +131 -23
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +73 -55
  9. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
  10. data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  12. data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
  13. data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -6
  14. data/lib/ruby_indexer/test/configuration_test.rb +116 -51
  15. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  16. data/lib/ruby_indexer/test/index_test.rb +72 -43
  17. data/lib/ruby_indexer/test/method_test.rb +80 -0
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  19. data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
  20. data/lib/ruby_indexer/test/test_case.rb +2 -2
  21. data/lib/ruby_indexer/test/uri_test.rb +72 -0
  22. data/lib/ruby_lsp/addon.rb +9 -0
  23. data/lib/ruby_lsp/base_server.rb +15 -6
  24. data/lib/ruby_lsp/document.rb +10 -1
  25. data/lib/ruby_lsp/global_state.rb +1 -1
  26. data/lib/ruby_lsp/internal.rb +1 -1
  27. data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
  28. data/lib/ruby_lsp/listeners/completion.rb +73 -4
  29. data/lib/ruby_lsp/listeners/definition.rb +73 -17
  30. data/lib/ruby_lsp/listeners/document_symbol.rb +49 -5
  31. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  32. data/lib/ruby_lsp/listeners/hover.rb +57 -0
  33. data/lib/ruby_lsp/requests/completion.rb +6 -0
  34. data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
  35. data/lib/ruby_lsp/requests/definition.rb +6 -0
  36. data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
  37. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
  38. data/lib/ruby_lsp/requests/rename.rb +14 -4
  39. data/lib/ruby_lsp/requests/support/common.rb +1 -5
  40. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
  42. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  43. data/lib/ruby_lsp/server.rb +42 -7
  44. data/lib/ruby_lsp/setup_bundler.rb +54 -46
  45. data/lib/ruby_lsp/test_helper.rb +45 -11
  46. data/lib/ruby_lsp/type_inferrer.rb +22 -0
  47. data/lib/ruby_lsp/utils.rb +3 -0
  48. metadata +7 -8
  49. 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
 
@@ -110,13 +116,14 @@ module RubyLsp
110
116
  name = 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
- range_from_location(node.location),
126
+ range,
120
127
  entries,
121
128
  top_level?(complete_name),
122
129
  )
@@ -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: range_from_location(location),
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
- matched_indexable_paths = @index.search_require_paths(path_node_to_complete.content)
440
+ matched_uris = @index.search_require_paths(path_node_to_complete.content)
372
441
 
373
- matched_indexable_paths.map!(&:require_path).sort!.each do |path|
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
@@ -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
 
@@ -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: URI::Generic.from_path(path: entry.file_path).to_s,
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: URI::Generic.from_path(path: entry.file_path).to_s,
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,11 @@ module RubyLsp
275
329
  return unless methods
276
330
 
277
331
  methods.each do |target_method|
278
- file_path = target_method.file_path
279
- next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(file_path)
332
+ uri = target_method.uri
333
+ next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(T.must(uri.full_path))
280
334
 
281
335
  @response_builder << Interface::LocationLink.new(
282
- target_uri: URI::Generic.from_path(path: file_path).to_s,
336
+ target_uri: uri.to_s,
283
337
  target_range: range_from_location(target_method.location),
284
338
  target_selection_range: range_from_location(target_method.name_location),
285
339
  )
@@ -290,20 +344,22 @@ module RubyLsp
290
344
  def handle_require_definition(node, message)
291
345
  case message
292
346
  when :require
293
- entry = @index.search_require_paths(node.content).find do |indexable_path|
294
- indexable_path.require_path == node.content
347
+ entry = @index.search_require_paths(node.content).find do |uri|
348
+ uri.require_path == node.content
295
349
  end
296
350
 
297
351
  if entry
298
352
  candidate = entry.full_path
299
353
 
300
- @response_builder << Interface::Location.new(
301
- uri: URI::Generic.from_path(path: candidate).to_s,
302
- range: Interface::Range.new(
303
- start: Interface::Position.new(line: 0, character: 0),
304
- end: Interface::Position.new(line: 0, character: 0),
305
- ),
306
- )
354
+ if candidate
355
+ @response_builder << Interface::Location.new(
356
+ uri: URI::Generic.from_path(path: candidate).to_s,
357
+ range: Interface::Range.new(
358
+ start: Interface::Position.new(line: 0, character: 0),
359
+ end: Interface::Position.new(line: 0, character: 0),
360
+ ),
361
+ )
362
+ end
307
363
  end
308
364
  when :require_relative
309
365
  required_file = "#{node.content}.rb"
@@ -346,11 +402,11 @@ module RubyLsp
346
402
  # If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
347
403
  # additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
348
404
  # ignore
349
- file_path = entry.file_path
350
- next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(file_path)
405
+ uri = entry.uri
406
+ next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(T.must(uri.full_path))
351
407
 
352
408
  @response_builder << Interface::LocationLink.new(
353
- target_uri: URI::Generic.from_path(path: file_path).to_s,
409
+ target_uri: uri.to_s,
354
410
  target_range: range_from_location(entry.location),
355
411
  target_selection_range: range_from_location(entry.name_location),
356
412
  )
@@ -41,6 +41,10 @@ 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,
45
+ :on_instance_variable_operator_write_node_enter,
46
+ :on_instance_variable_or_write_node_enter,
47
+ :on_instance_variable_and_write_node_enter,
44
48
  :on_class_variable_write_node_enter,
45
49
  :on_singleton_class_node_enter,
46
50
  :on_singleton_class_node_leave,
@@ -249,21 +253,61 @@ module RubyLsp
249
253
  @response_builder.pop
250
254
  end
251
255
 
256
+ sig { params(node: Prism::ClassVariableWriteNode).void }
257
+ def on_class_variable_write_node_enter(node)
258
+ create_document_symbol(
259
+ name: node.name.to_s,
260
+ kind: Constant::SymbolKind::VARIABLE,
261
+ range_location: node.name_loc,
262
+ selection_range_location: node.name_loc,
263
+ )
264
+ end
265
+
252
266
  sig { params(node: Prism::InstanceVariableWriteNode).void }
253
267
  def on_instance_variable_write_node_enter(node)
254
268
  create_document_symbol(
255
269
  name: node.name.to_s,
256
- kind: Constant::SymbolKind::VARIABLE,
270
+ kind: Constant::SymbolKind::FIELD,
257
271
  range_location: node.name_loc,
258
272
  selection_range_location: node.name_loc,
259
273
  )
260
274
  end
261
275
 
262
- sig { params(node: Prism::ClassVariableWriteNode).void }
263
- def on_class_variable_write_node_enter(node)
276
+ sig { params(node: Prism::InstanceVariableTargetNode).void }
277
+ def on_instance_variable_target_node_enter(node)
264
278
  create_document_symbol(
265
279
  name: node.name.to_s,
266
- kind: Constant::SymbolKind::VARIABLE,
280
+ kind: Constant::SymbolKind::FIELD,
281
+ range_location: node.location,
282
+ selection_range_location: node.location,
283
+ )
284
+ end
285
+
286
+ sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
287
+ def on_instance_variable_operator_write_node_enter(node)
288
+ create_document_symbol(
289
+ name: node.name.to_s,
290
+ kind: Constant::SymbolKind::FIELD,
291
+ range_location: node.name_loc,
292
+ selection_range_location: node.name_loc,
293
+ )
294
+ end
295
+
296
+ sig { params(node: Prism::InstanceVariableOrWriteNode).void }
297
+ def on_instance_variable_or_write_node_enter(node)
298
+ create_document_symbol(
299
+ name: node.name.to_s,
300
+ kind: Constant::SymbolKind::FIELD,
301
+ range_location: node.name_loc,
302
+ selection_range_location: node.name_loc,
303
+ )
304
+ end
305
+
306
+ sig { params(node: Prism::InstanceVariableAndWriteNode).void }
307
+ def on_instance_variable_and_write_node_enter(node)
308
+ create_document_symbol(
309
+ name: node.name.to_s,
310
+ kind: Constant::SymbolKind::FIELD,
267
311
  range_location: node.name_loc,
268
312
  selection_range_location: node.name_loc,
269
313
  )
@@ -296,7 +340,7 @@ module RubyLsp
296
340
  ).returns(Interface::DocumentSymbol)
297
341
  end
298
342
  def create_document_symbol(name:, kind:, range_location:, selection_range_location:)
299
- name = "<blank>" if name.empty?
343
+ name = "<blank>" if name.strip.empty?
300
344
  symbol = Interface::DocumentSymbol.new(
301
345
  name: name,
302
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
 
@@ -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)
@@ -62,6 +62,12 @@ module RubyLsp
62
62
  Prism::InstanceVariableOrWriteNode,
63
63
  Prism::InstanceVariableTargetNode,
64
64
  Prism::InstanceVariableWriteNode,
65
+ Prism::ClassVariableAndWriteNode,
66
+ Prism::ClassVariableOperatorWriteNode,
67
+ Prism::ClassVariableOrWriteNode,
68
+ Prism::ClassVariableReadNode,
69
+ Prism::ClassVariableTargetNode,
70
+ Prism::ClassVariableWriteNode,
65
71
  ],
66
72
  code_units_cache: document.code_units_cache,
67
73
  )
@@ -49,7 +49,8 @@ module RubyLsp
49
49
  if owner_name
50
50
  entries = entries.select do |entry|
51
51
  (entry.is_a?(RubyIndexer::Entry::Member) || entry.is_a?(RubyIndexer::Entry::InstanceVariable) ||
52
- entry.is_a?(RubyIndexer::Entry::MethodAlias)) && entry.owner&.name == owner_name
52
+ entry.is_a?(RubyIndexer::Entry::MethodAlias) || entry.is_a?(RubyIndexer::Entry::ClassVariable)) &&
53
+ entry.owner&.name == owner_name
53
54
  end
54
55
  end
55
56
 
@@ -56,6 +56,12 @@ module RubyLsp
56
56
  Prism::StringNode,
57
57
  Prism::SuperNode,
58
58
  Prism::ForwardingSuperNode,
59
+ Prism::ClassVariableAndWriteNode,
60
+ Prism::ClassVariableOperatorWriteNode,
61
+ Prism::ClassVariableOrWriteNode,
62
+ Prism::ClassVariableReadNode,
63
+ Prism::ClassVariableTargetNode,
64
+ Prism::ClassVariableWriteNode,
59
65
  ],
60
66
  code_units_cache: document.code_units_cache,
61
67
  )
@@ -0,0 +1,51 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # The
7
+ # [prepare_rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename)
8
+ # # request checks the validity of a rename operation at a given location.
9
+ class PrepareRename < Request
10
+ extend T::Sig
11
+ include Support::Common
12
+
13
+ sig do
14
+ params(
15
+ document: RubyDocument,
16
+ position: T::Hash[Symbol, T.untyped],
17
+ ).void
18
+ end
19
+ def initialize(document, position)
20
+ super()
21
+ @document = document
22
+ @position = T.let(position, T::Hash[Symbol, Integer])
23
+ end
24
+
25
+ sig { override.returns(T.nilable(Interface::Range)) }
26
+ def perform
27
+ char_position = @document.create_scanner.find_char_position(@position)
28
+
29
+ node_context = RubyDocument.locate(
30
+ @document.parse_result.value,
31
+ char_position,
32
+ node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
33
+ code_units_cache: @document.code_units_cache,
34
+ )
35
+ target = node_context.node
36
+ parent = node_context.parent
37
+ return if !target || target.is_a?(Prism::ProgramNode)
38
+
39
+ if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
40
+ target = determine_target(
41
+ target,
42
+ parent,
43
+ @position,
44
+ )
45
+ end
46
+
47
+ range_from_location(target.location)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -66,7 +66,7 @@ module RubyLsp
66
66
  Interface::TypeHierarchyItem.new(
67
67
  name: first_entry.name,
68
68
  kind: kind_for_entry(first_entry),
69
- uri: URI::Generic.from_path(path: first_entry.file_path).to_s,
69
+ uri: first_entry.uri.to_s,
70
70
  range: range,
71
71
  selection_range: range,
72
72
  ),
@@ -12,6 +12,15 @@ module RubyLsp
12
12
 
13
13
  class InvalidNameError < StandardError; end
14
14
 
15
+ class << self
16
+ extend T::Sig
17
+
18
+ sig { returns(Interface::RenameOptions) }
19
+ def provider
20
+ Interface::RenameOptions.new(prepare_provider: true)
21
+ end
22
+ end
23
+
15
24
  sig do
16
25
  params(
17
26
  global_state: GlobalState,
@@ -106,7 +115,9 @@ module RubyLsp
106
115
 
107
116
  T.must(@global_state.index[fully_qualified_name]).each do |entry|
108
117
  # Do not rename files that are not part of the workspace
109
- next unless entry.file_path.start_with?(@global_state.workspace_path)
118
+ uri = entry.uri
119
+ file_path = T.must(uri.full_path)
120
+ next unless file_path.start_with?(@global_state.workspace_path)
110
121
 
111
122
  case entry
112
123
  when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
@@ -117,13 +128,12 @@ module RubyLsp
117
128
  if "#{file_name}.rb" == entry.file_name
118
129
  new_file_name = file_from_constant_name(T.must(@new_name.split("::").last))
119
130
 
120
- old_uri = URI::Generic.from_path(path: entry.file_path).to_s
121
131
  new_uri = URI::Generic.from_path(path: File.join(
122
- File.dirname(entry.file_path),
132
+ File.dirname(file_path),
123
133
  "#{new_file_name}.rb",
124
134
  )).to_s
125
135
 
126
- document_changes << Interface::RenameFile.new(kind: "rename", old_uri: old_uri, new_uri: new_uri)
136
+ document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
127
137
  end
128
138
  end
129
139
  end
@@ -93,11 +93,7 @@ module RubyLsp
93
93
  # based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
94
94
  # columns. The format for VS Code file URIs is
95
95
  # `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
96
- uri = URI::Generic.from_path(
97
- path: entry.file_path,
98
- fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
99
- )
100
-
96
+ uri = "#{entry.uri}#L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}"
101
97
  definitions << "[#{entry.file_name}](#{uri})"
102
98
  content << "\n\n#{entry.comments}" unless entry.comments.empty?
103
99
  end
@@ -65,7 +65,7 @@ module RubyLsp
65
65
  Interface::TypeHierarchyItem.new(
66
66
  name: entry.name,
67
67
  kind: kind_for_entry(entry),
68
- uri: URI::Generic.from_path(path: entry.file_path).to_s,
68
+ uri: entry.uri.to_s,
69
69
  range: range_from_location(entry.location),
70
70
  selection_range: range_from_location(entry.name_location),
71
71
  detail: entry.file_name,
@@ -21,7 +21,8 @@ module RubyLsp
21
21
  sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
22
22
  def perform
23
23
  @index.fuzzy_search(@query).filter_map do |entry|
24
- file_path = entry.file_path
24
+ uri = entry.uri
25
+ file_path = T.must(uri.full_path)
25
26
 
26
27
  # We only show symbols declared in the workspace
27
28
  in_dependencies = !not_in_dependencies?(file_path)
@@ -43,7 +44,7 @@ module RubyLsp
43
44
  container_name: container.join("::"),
44
45
  kind: kind,
45
46
  location: Interface::Location.new(
46
- uri: URI::Generic.from_path(path: file_path).to_s,
47
+ uri: uri.to_s,
47
48
  range: Interface::Range.new(
48
49
  start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
49
50
  end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
@@ -5,7 +5,7 @@ def compose(raw_initialize)
5
5
  require_relative "../setup_bundler"
6
6
  require "json"
7
7
  require "uri"
8
- require_relative "../../core_ext/uri"
8
+ require "ruby_indexer/lib/ruby_indexer/uri"
9
9
 
10
10
  initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
11
11
  workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)