ruby-lsp 0.16.7 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/VERSION +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +139 -18
  5. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +101 -12
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +183 -10
  7. data/lib/ruby_indexer/test/classes_and_modules_test.rb +55 -9
  8. data/lib/ruby_indexer/test/configuration_test.rb +4 -5
  9. data/lib/ruby_indexer/test/constant_test.rb +8 -8
  10. data/lib/ruby_indexer/test/index_test.rb +528 -0
  11. data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
  12. data/lib/ruby_indexer/test/method_test.rb +37 -0
  13. data/lib/ruby_indexer/test/test_case.rb +3 -1
  14. data/lib/ruby_lsp/addon.rb +8 -8
  15. data/lib/ruby_lsp/document.rb +3 -3
  16. data/lib/ruby_lsp/internal.rb +1 -0
  17. data/lib/ruby_lsp/listeners/completion.rb +74 -17
  18. data/lib/ruby_lsp/listeners/definition.rb +62 -6
  19. data/lib/ruby_lsp/listeners/hover.rb +60 -6
  20. data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
  21. data/lib/ruby_lsp/node_context.rb +28 -0
  22. data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
  23. data/lib/ruby_lsp/requests/code_actions.rb +16 -15
  24. data/lib/ruby_lsp/requests/completion.rb +21 -13
  25. data/lib/ruby_lsp/requests/completion_resolve.rb +9 -0
  26. data/lib/ruby_lsp/requests/definition.rb +20 -5
  27. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  28. data/lib/ruby_lsp/requests/hover.rb +5 -6
  29. data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
  30. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  31. data/lib/ruby_lsp/requests/support/common.rb +4 -3
  32. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -1
  33. data/lib/ruby_lsp/server.rb +10 -4
  34. data/lib/ruby_lsp/test_helper.rb +1 -0
  35. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebc166637de670ea02144e74ccb2e99ae1a9824c9f99995380d81bed99ff6a29
4
- data.tar.gz: 48b7ef9364f4d1a8044042c8476414e87da5b38ca4871683c16d06636fc8e411
3
+ metadata.gz: 61194ff211ddd14d6598752e0a60409c16c2c5460c4d007ac1e1bc7e56b50116
4
+ data.tar.gz: 29ed28f4392929f09a4ba4ea6a19612f9dfc3e2c0b2641c551627ac69a32b3c0
5
5
  SHA512:
6
- metadata.gz: 80c4f791e7dfef66623f5190eaba4dc76c745a27f74238d912c34711c408803ec952d5ec3e8041ebb6410b99c74d2f3d8274db5dffa8f187e48a7032a3960086
7
- data.tar.gz: 941209321fb263b9a22cbe27102e8761c6ef127902e2b74f760fec1a8701d96e5bcef5f01c704e7215e061437cac2577c38e100464cccfe03909aa42ee1e318e
6
+ metadata.gz: 56420391d81fe9e9c06a7fda2a04b1f99ac6fbb10211f064bfd65f36dd9148658d47743be437eb40240d5522786ec0566d0cea763b964630fb5ea67fe552687b
7
+ data.tar.gz: f760fad5d7abf0d0071badf6dd04f82e2266a64b0122d3a08cd5c3c799be15e377c94da93a4e8164e1f6ae04350e5ca05b16afbb7a8e341a8e80590053728ed7
data/README.md CHANGED
@@ -50,7 +50,7 @@ editor. Do not install the `ruby-lsp` gem manually.
50
50
 
51
51
  ### With other editors
52
52
 
53
- See [editors](EDITORS.md) for community instructions on setting up the Ruby LSP.
53
+ See [editors](EDITORS.md) for community instructions on setting up the Ruby LSP, which current includes Emacs, Neovim, Sublime Text, and Zed.
54
54
 
55
55
  The gem can be installed by doing
56
56
  ```shell
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.16.7
1
+ 0.17.1
@@ -11,6 +11,7 @@ module RubyIndexer
11
11
  def initialize(index, dispatcher, parse_result, file_path)
12
12
  @index = index
13
13
  @file_path = file_path
14
+ @visibility_stack = T.let([Entry::Visibility::PUBLIC], T::Array[Entry::Visibility])
14
15
  @comments_by_line = T.let(
15
16
  parse_result.comments.to_h do |c|
16
17
  [c.location.start_line, c]
@@ -35,6 +36,7 @@ module RubyIndexer
35
36
  :on_def_node_enter,
36
37
  :on_def_node_leave,
37
38
  :on_call_node_enter,
39
+ :on_call_node_leave,
38
40
  :on_multi_write_node_enter,
39
41
  :on_constant_path_write_node_enter,
40
42
  :on_constant_path_or_write_node_enter,
@@ -45,11 +47,17 @@ module RubyIndexer
45
47
  :on_constant_or_write_node_enter,
46
48
  :on_constant_and_write_node_enter,
47
49
  :on_constant_operator_write_node_enter,
50
+ :on_instance_variable_write_node_enter,
51
+ :on_instance_variable_and_write_node_enter,
52
+ :on_instance_variable_operator_write_node_enter,
53
+ :on_instance_variable_or_write_node_enter,
54
+ :on_instance_variable_target_node_enter,
48
55
  )
49
56
  end
50
57
 
51
58
  sig { params(node: Prism::ClassNode).void }
52
59
  def on_class_node_enter(node)
60
+ @visibility_stack.push(Entry::Visibility::PUBLIC)
53
61
  name = node.constant_path.location.slice
54
62
 
55
63
  comments = collect_comments(node)
@@ -60,8 +68,10 @@ module RubyIndexer
60
68
  superclass.slice
61
69
  end
62
70
 
71
+ nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
72
+
63
73
  entry = Entry::Class.new(
64
- fully_qualify_name(name),
74
+ nesting,
65
75
  @file_path,
66
76
  node.location,
67
77
  comments,
@@ -77,14 +87,18 @@ module RubyIndexer
77
87
  def on_class_node_leave(node)
78
88
  @stack.pop
79
89
  @owner_stack.pop
90
+ @visibility_stack.pop
80
91
  end
81
92
 
82
93
  sig { params(node: Prism::ModuleNode).void }
83
94
  def on_module_node_enter(node)
95
+ @visibility_stack.push(Entry::Visibility::PUBLIC)
84
96
  name = node.constant_path.location.slice
85
97
 
86
98
  comments = collect_comments(node)
87
- entry = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
99
+
100
+ nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
101
+ entry = Entry::Module.new(nesting, @file_path, node.location, comments)
88
102
 
89
103
  @owner_stack << entry
90
104
  @index << entry
@@ -95,6 +109,7 @@ module RubyIndexer
95
109
  def on_module_node_leave(node)
96
110
  @stack.pop
97
111
  @owner_stack.pop
112
+ @visibility_stack.pop
98
113
  end
99
114
 
100
115
  sig { params(node: Prism::MultiWriteNode).void }
@@ -194,10 +209,27 @@ module RubyIndexer
194
209
  handle_attribute(node, reader: false, writer: true)
195
210
  when :attr_accessor
196
211
  handle_attribute(node, reader: true, writer: true)
197
- when :include
198
- handle_module_operation(node, :included_modules)
199
- when :prepend
200
- handle_module_operation(node, :prepended_modules)
212
+ when :include, :prepend, :extend
213
+ handle_module_operation(node, message)
214
+ when :public
215
+ @visibility_stack.push(Entry::Visibility::PUBLIC)
216
+ when :protected
217
+ @visibility_stack.push(Entry::Visibility::PROTECTED)
218
+ when :private
219
+ @visibility_stack.push(Entry::Visibility::PRIVATE)
220
+ end
221
+ end
222
+
223
+ sig { params(node: Prism::CallNode).void }
224
+ def on_call_node_leave(node)
225
+ message = node.name
226
+ case message
227
+ when :public, :protected, :private
228
+ # We want to restore the visibility stack when we leave a method definition with a visibility modifier
229
+ # e.g. `private def foo; end`
230
+ if node.arguments&.arguments&.first&.is_a?(Prism::DefNode)
231
+ @visibility_stack.pop
232
+ end
201
233
  end
202
234
  end
203
235
 
@@ -206,6 +238,7 @@ module RubyIndexer
206
238
  @inside_def = true
207
239
  method_name = node.name.to_s
208
240
  comments = collect_comments(node)
241
+
209
242
  case node.receiver
210
243
  when nil
211
244
  @index << Entry::InstanceMethod.new(
@@ -214,6 +247,7 @@ module RubyIndexer
214
247
  node.location,
215
248
  comments,
216
249
  node.parameters,
250
+ current_visibility,
217
251
  @owner_stack.last,
218
252
  )
219
253
  when Prism::SelfNode
@@ -223,6 +257,7 @@ module RubyIndexer
223
257
  node.location,
224
258
  comments,
225
259
  node.parameters,
260
+ current_visibility,
226
261
  @owner_stack.last,
227
262
  )
228
263
  end
@@ -233,6 +268,76 @@ module RubyIndexer
233
268
  @inside_def = false
234
269
  end
235
270
 
271
+ sig { params(node: Prism::InstanceVariableWriteNode).void }
272
+ def on_instance_variable_write_node_enter(node)
273
+ name = node.name.to_s
274
+ return if name == "@"
275
+
276
+ @index << Entry::InstanceVariable.new(
277
+ name,
278
+ @file_path,
279
+ node.name_loc,
280
+ collect_comments(node),
281
+ @owner_stack.last,
282
+ )
283
+ end
284
+
285
+ sig { params(node: Prism::InstanceVariableAndWriteNode).void }
286
+ def on_instance_variable_and_write_node_enter(node)
287
+ name = node.name.to_s
288
+ return if name == "@"
289
+
290
+ @index << Entry::InstanceVariable.new(
291
+ name,
292
+ @file_path,
293
+ node.name_loc,
294
+ collect_comments(node),
295
+ @owner_stack.last,
296
+ )
297
+ end
298
+
299
+ sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
300
+ def on_instance_variable_operator_write_node_enter(node)
301
+ name = node.name.to_s
302
+ return if name == "@"
303
+
304
+ @index << Entry::InstanceVariable.new(
305
+ name,
306
+ @file_path,
307
+ node.name_loc,
308
+ collect_comments(node),
309
+ @owner_stack.last,
310
+ )
311
+ end
312
+
313
+ sig { params(node: Prism::InstanceVariableOrWriteNode).void }
314
+ def on_instance_variable_or_write_node_enter(node)
315
+ name = node.name.to_s
316
+ return if name == "@"
317
+
318
+ @index << Entry::InstanceVariable.new(
319
+ name,
320
+ @file_path,
321
+ node.name_loc,
322
+ collect_comments(node),
323
+ @owner_stack.last,
324
+ )
325
+ end
326
+
327
+ sig { params(node: Prism::InstanceVariableTargetNode).void }
328
+ def on_instance_variable_target_node_enter(node)
329
+ name = node.name.to_s
330
+ return if name == "@"
331
+
332
+ @index << Entry::InstanceVariable.new(
333
+ name,
334
+ @file_path,
335
+ node.location,
336
+ collect_comments(node),
337
+ @owner_stack.last,
338
+ )
339
+ end
340
+
236
341
  private
237
342
 
238
343
  sig { params(node: Prism::CallNode).void }
@@ -257,7 +362,7 @@ module RubyIndexer
257
362
  # The private_constant method does not resolve the constant name. It always points to a constant that needs to
258
363
  # exist in the current namespace
259
364
  entries = @index[fully_qualify_name(name)]
260
- entries&.each { |entry| entry.visibility = :private }
365
+ entries&.each { |entry| entry.visibility = Entry::Visibility::PRIVATE }
261
366
  end
262
367
 
263
368
  sig do
@@ -354,8 +459,15 @@ module RubyIndexer
354
459
 
355
460
  next unless name && loc
356
461
 
357
- @index << Entry::Accessor.new(name, @file_path, loc, comments, @owner_stack.last) if reader
358
- @index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @owner_stack.last) if writer
462
+ @index << Entry::Accessor.new(name, @file_path, loc, comments, current_visibility, @owner_stack.last) if reader
463
+ @index << Entry::Accessor.new(
464
+ "#{name}=",
465
+ @file_path,
466
+ loc,
467
+ comments,
468
+ current_visibility,
469
+ @owner_stack.last,
470
+ ) if writer
359
471
  end
360
472
  end
361
473
 
@@ -369,17 +481,26 @@ module RubyIndexer
369
481
  arguments = node.arguments&.arguments
370
482
  return unless arguments
371
483
 
372
- names = arguments.filter_map do |node|
373
- if node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
374
- node.full_name
484
+ arguments.each do |node|
485
+ next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
486
+
487
+ case operation
488
+ when :include
489
+ owner.mixin_operations << Entry::Include.new(node.full_name)
490
+ when :prepend
491
+ owner.mixin_operations << Entry::Prepend.new(node.full_name)
492
+ when :extend
493
+ owner.mixin_operations << Entry::Extend.new(node.full_name)
375
494
  end
376
- rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError
377
- # TO DO: add MissingNodesInConstantPathError when released in Prism
378
- # If a constant path reference is dynamic or missing parts, we can't
379
- # index it
495
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
496
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
497
+ # Do nothing
380
498
  end
381
- collection = operation == :included_modules ? owner.included_modules : owner.prepended_modules
382
- collection.concat(names)
499
+ end
500
+
501
+ sig { returns(Entry::Visibility) }
502
+ def current_visibility
503
+ T.must(@visibility_stack.last)
383
504
  end
384
505
  end
385
506
  end
@@ -3,6 +3,14 @@
3
3
 
4
4
  module RubyIndexer
5
5
  class Entry
6
+ class Visibility < T::Enum
7
+ enums do
8
+ PUBLIC = new(:public)
9
+ PROTECTED = new(:protected)
10
+ PRIVATE = new(:private)
11
+ end
12
+ end
13
+
6
14
  extend T::Sig
7
15
 
8
16
  sig { returns(String) }
@@ -17,7 +25,7 @@ module RubyIndexer
17
25
  sig { returns(T::Array[String]) }
18
26
  attr_reader :comments
19
27
 
20
- sig { returns(Symbol) }
28
+ sig { returns(Visibility) }
21
29
  attr_accessor :visibility
22
30
 
23
31
  sig do
@@ -32,7 +40,7 @@ module RubyIndexer
32
40
  @name = name
33
41
  @file_path = file_path
34
42
  @comments = comments
35
- @visibility = T.let(:public, Symbol)
43
+ @visibility = T.let(Visibility::PUBLIC, Visibility)
36
44
 
37
45
  @location = T.let(
38
46
  if location.is_a?(Prism::Location)
@@ -49,11 +57,35 @@ module RubyIndexer
49
57
  )
50
58
  end
51
59
 
60
+ sig { returns(T::Boolean) }
61
+ def private?
62
+ visibility == Visibility::PRIVATE
63
+ end
64
+
52
65
  sig { returns(String) }
53
66
  def file_name
54
67
  File.basename(@file_path)
55
68
  end
56
69
 
70
+ class ModuleOperation
71
+ extend T::Sig
72
+ extend T::Helpers
73
+
74
+ abstract!
75
+
76
+ sig { returns(String) }
77
+ attr_reader :module_name
78
+
79
+ sig { params(module_name: String).void }
80
+ def initialize(module_name)
81
+ @module_name = module_name
82
+ end
83
+ end
84
+
85
+ class Include < ModuleOperation; end
86
+ class Prepend < ModuleOperation; end
87
+ class Extend < ModuleOperation; end
88
+
57
89
  class Namespace < Entry
58
90
  extend T::Sig
59
91
  extend T::Helpers
@@ -61,13 +93,40 @@ module RubyIndexer
61
93
  abstract!
62
94
 
63
95
  sig { returns(T::Array[String]) }
64
- def included_modules
65
- @included_modules ||= T.let([], T.nilable(T::Array[String]))
96
+ attr_reader :nesting
97
+
98
+ sig do
99
+ params(
100
+ nesting: T::Array[String],
101
+ file_path: String,
102
+ location: T.any(Prism::Location, RubyIndexer::Location),
103
+ comments: T::Array[String],
104
+ ).void
105
+ end
106
+ def initialize(nesting, file_path, location, comments)
107
+ @name = T.let(nesting.join("::"), String)
108
+ # The original nesting where this namespace was discovered
109
+ @nesting = nesting
110
+
111
+ super(@name, file_path, location, comments)
66
112
  end
67
113
 
68
114
  sig { returns(T::Array[String]) }
69
- def prepended_modules
70
- @prepended_modules ||= T.let([], T.nilable(T::Array[String]))
115
+ def mixin_operation_module_names
116
+ mixin_operations.map(&:module_name)
117
+ end
118
+
119
+ # Stores all explicit prepend, include and extend operations in the exact order they were discovered in the source
120
+ # code. Maintaining the order is essential to linearize ancestors the right way when a module is both included
121
+ # and prepended
122
+ sig { returns(T::Array[ModuleOperation]) }
123
+ def mixin_operations
124
+ @mixin_operations ||= T.let([], T.nilable(T::Array[ModuleOperation]))
125
+ end
126
+
127
+ sig { returns(Integer) }
128
+ def ancestor_hash
129
+ mixin_operation_module_names.hash
71
130
  end
72
131
  end
73
132
 
@@ -84,17 +143,23 @@ module RubyIndexer
84
143
 
85
144
  sig do
86
145
  params(
87
- name: String,
146
+ nesting: T::Array[String],
88
147
  file_path: String,
89
148
  location: T.any(Prism::Location, RubyIndexer::Location),
90
149
  comments: T::Array[String],
91
150
  parent_class: T.nilable(String),
92
151
  ).void
93
152
  end
94
- def initialize(name, file_path, location, comments, parent_class)
95
- super(name, file_path, location, comments)
153
+ def initialize(nesting, file_path, location, comments, parent_class)
154
+ super(nesting, file_path, location, comments)
155
+
96
156
  @parent_class = T.let(parent_class, T.nilable(String))
97
157
  end
158
+
159
+ sig { override.returns(Integer) }
160
+ def ancestor_hash
161
+ [mixin_operation_module_names, @parent_class].hash
162
+ end
98
163
  end
99
164
 
100
165
  class Constant < Entry
@@ -188,11 +253,13 @@ module RubyIndexer
188
253
  file_path: String,
189
254
  location: T.any(Prism::Location, RubyIndexer::Location),
190
255
  comments: T::Array[String],
256
+ visibility: Visibility,
191
257
  owner: T.nilable(Entry::Namespace),
192
258
  ).void
193
259
  end
194
- def initialize(name, file_path, location, comments, owner)
260
+ def initialize(name, file_path, location, comments, visibility, owner) # rubocop:disable Metrics/ParameterLists
195
261
  super(name, file_path, location, comments)
262
+ @visibility = visibility
196
263
  @owner = owner
197
264
  end
198
265
 
@@ -227,11 +294,12 @@ module RubyIndexer
227
294
  location: T.any(Prism::Location, RubyIndexer::Location),
228
295
  comments: T::Array[String],
229
296
  parameters_node: T.nilable(Prism::ParametersNode),
297
+ visibility: Visibility,
230
298
  owner: T.nilable(Entry::Namespace),
231
299
  ).void
232
300
  end
233
- def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
234
- super(name, file_path, location, comments, owner)
301
+ def initialize(name, file_path, location, comments, parameters_node, visibility, owner) # rubocop:disable Metrics/ParameterLists
302
+ super(name, file_path, location, comments, visibility, owner)
235
303
 
236
304
  @parameters = T.let(list_params(parameters_node), T::Array[Parameter])
237
305
  end
@@ -377,8 +445,29 @@ module RubyIndexer
377
445
  def initialize(target, unresolved_alias)
378
446
  super(unresolved_alias.name, unresolved_alias.file_path, unresolved_alias.location, unresolved_alias.comments)
379
447
 
448
+ @visibility = unresolved_alias.visibility
380
449
  @target = target
381
450
  end
382
451
  end
452
+
453
+ # Represents an instance variable e.g.: @a = 1
454
+ class InstanceVariable < Entry
455
+ sig { returns(T.nilable(Entry::Namespace)) }
456
+ attr_reader :owner
457
+
458
+ sig do
459
+ params(
460
+ name: String,
461
+ file_path: String,
462
+ location: T.any(Prism::Location, RubyIndexer::Location),
463
+ comments: T::Array[String],
464
+ owner: T.nilable(Entry::Namespace),
465
+ ).void
466
+ end
467
+ def initialize(name, file_path, location, comments, owner)
468
+ super(name, file_path, location, comments)
469
+ @owner = owner
470
+ end
471
+ end
383
472
  end
384
473
  end