ruby-lsp 0.22.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
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)