ruby-lsp 0.16.6 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +21 -4
  5. data/exe/ruby-lsp-check +1 -3
  6. data/exe/ruby-lsp-doctor +1 -4
  7. data/lib/core_ext/uri.rb +3 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/{collector.rb → declaration_listener.rb} +258 -140
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +101 -12
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +187 -12
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +106 -10
  13. data/lib/ruby_indexer/test/configuration_test.rb +4 -5
  14. data/lib/ruby_indexer/test/constant_test.rb +11 -8
  15. data/lib/ruby_indexer/test/index_test.rb +528 -0
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
  17. data/lib/ruby_indexer/test/method_test.rb +93 -0
  18. data/lib/ruby_indexer/test/test_case.rb +3 -1
  19. data/lib/ruby_lsp/addon.rb +8 -8
  20. data/lib/ruby_lsp/document.rb +3 -3
  21. data/lib/ruby_lsp/internal.rb +1 -0
  22. data/lib/ruby_lsp/listeners/code_lens.rb +11 -0
  23. data/lib/ruby_lsp/listeners/completion.rb +144 -51
  24. data/lib/ruby_lsp/listeners/definition.rb +77 -12
  25. data/lib/ruby_lsp/listeners/document_highlight.rb +1 -1
  26. data/lib/ruby_lsp/listeners/document_link.rb +1 -1
  27. data/lib/ruby_lsp/listeners/hover.rb +60 -6
  28. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +59 -3
  29. data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
  30. data/lib/ruby_lsp/node_context.rb +28 -0
  31. data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
  32. data/lib/ruby_lsp/requests/code_actions.rb +16 -15
  33. data/lib/ruby_lsp/requests/completion.rb +22 -13
  34. data/lib/ruby_lsp/requests/completion_resolve.rb +26 -10
  35. data/lib/ruby_lsp/requests/definition.rb +21 -5
  36. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  37. data/lib/ruby_lsp/requests/hover.rb +5 -6
  38. data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
  39. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  40. data/lib/ruby_lsp/requests/support/common.rb +20 -1
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -1
  42. data/lib/ruby_lsp/server.rb +10 -4
  43. metadata +10 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d725a0a2fbd58820e2a87efae85564498221feb8410292055b0caafd889284e6
4
- data.tar.gz: 5376ca8a9d08ca0883fca3aa19a1a178102eb60f49ab3fe5f07bb1a58ba63ddf
3
+ metadata.gz: 3060dd91df98bd5412edff61c4f4df8d14d2f3be8c92e33e138cbf078e44ee72
4
+ data.tar.gz: 8e30d6027e4d2ccd2466c756519a79f53787d9700a43819d985886bf2eaaa721
5
5
  SHA512:
6
- metadata.gz: 81fbb2e5018ad55d8888c98e277e7ac6ed8b45314627b1a3e3dabde2cb8aac0284dc70487a694226e5a08817feb82b64daadfcb955e9642b288e575189a56015
7
- data.tar.gz: f32ef084e4412752cc32df55da0dd7b985d3826db110bf91ba3473773043c50345ceb597432483a0a75f1790cabfca796fa9581d3ba86edff96a8642fb2312f2
6
+ metadata.gz: cda5116c872c174850448251864538357c6344f6d902f673f99cc9276a70034f9d446bba3b8e8465c4c26b0964c918afd186188cba00d8fe3b95de10b97fab20
7
+ data.tar.gz: b56637e2becb36b8df37847d44d743f2dc89c9db602e33d66ed4a54920f4f66bddec5017abdefb2ecee86bf35762c4a69bfb3e419c351fdfd17f8ddcfacd1b67
data/README.md CHANGED
@@ -33,7 +33,7 @@ The Ruby LSP features include
33
33
  - Completion for classes, modules, constants and require paths
34
34
  - Fuzzy search classes, modules and constants anywhere in the project and its dependencies (workspace symbol)
35
35
 
36
- Adding method support for definition, completion, hover and workspace symbol is planned, but not yet completed.
36
+ Adding method support for definition, completion, hover and workspace symbol is partially supported, but not yet complete. Follow progress in https://github.com/Shopify/ruby-lsp/issues/899
37
37
 
38
38
  See complete information about features [here](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
39
39
 
@@ -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.6
1
+ 0.17.0
data/exe/ruby-lsp CHANGED
@@ -18,6 +18,10 @@ parser = OptionParser.new do |opts|
18
18
  options[:debug] = true
19
19
  end
20
20
 
21
+ opts.on("--time-index", "Measure the time it takes to index the project") do
22
+ options[:time_index] = true
23
+ end
24
+
21
25
  opts.on(
22
26
  "--branch [BRANCH]",
23
27
  "Launch the Ruby LSP using the specified branch rather than the release version",
@@ -80,14 +84,27 @@ if options[:debug]
80
84
  end
81
85
 
82
86
  begin
83
- original_stdout = $stdout
84
- $stdout = $stderr
87
+ ENV.delete("RUBY_DEBUG_IRB_CONSOLE")
85
88
  require "debug/open_nonstop"
86
89
  rescue LoadError
87
90
  $stderr.puts("You need to install the debug gem to use the --debug flag")
88
- ensure
89
- $stdout = original_stdout
90
91
  end
91
92
  end
92
93
 
94
+ if options[:time_index]
95
+ require "benchmark"
96
+
97
+ index = RubyIndexer::Index.new
98
+
99
+ result = Benchmark.realtime { index.index_all }
100
+ entries = index.instance_variable_get(:@entries)
101
+ entries_by_entry_type = entries.values.flatten.group_by(&:class)
102
+
103
+ puts <<~MSG
104
+ Ruby LSP v#{RubyLsp::VERSION}: Indexing took #{result.round(5)} seconds and generated:
105
+ - #{entries_by_entry_type.map { |k, v| "#{k.name.split("::").last}: #{v.size}" }.join("\n- ")}
106
+ MSG
107
+ return
108
+ end
109
+
93
110
  RubyLsp::Server.new.start
data/exe/ruby-lsp-check CHANGED
@@ -47,9 +47,7 @@ index = RubyIndexer::Index.new
47
47
  indexables = RubyIndexer.configuration.indexables
48
48
 
49
49
  indexables.each_with_index do |indexable, i|
50
- result = Prism.parse(File.read(indexable.full_path))
51
- collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
52
- collector.collect(result.value)
50
+ index.index_single(indexable)
53
51
  rescue => e
54
52
  errors[indexable.full_path] = e
55
53
  ensure
data/exe/ruby-lsp-doctor CHANGED
@@ -19,8 +19,5 @@ puts "Globbing for indexable files"
19
19
 
20
20
  RubyIndexer.configuration.indexables.each do |indexable|
21
21
  puts "indexing: #{indexable.full_path}"
22
- content = File.read(indexable.full_path)
23
- result = Prism.parse(content)
24
- collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
25
- collector.collect(result.value)
22
+ index.index_single(indexable)
26
23
  end
data/lib/core_ext/uri.rb CHANGED
@@ -11,6 +11,9 @@ module URI
11
11
  # On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI
12
12
  escaped_path = if /^[A-Z]:/i.match?(path)
13
13
  DEFAULT_PARSER.escape("/#{path}")
14
+ elsif path.start_with?("//?/")
15
+ # Some paths on Windows start with "//?/". This is a special prefix that allows for long file paths
16
+ DEFAULT_PARSER.escape(path.delete_prefix("//?"))
14
17
  else
15
18
  DEFAULT_PARSER.escape(path)
16
19
  end
@@ -2,79 +2,118 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyIndexer
5
- class Collector
5
+ class DeclarationListener
6
6
  extend T::Sig
7
7
 
8
- LEAVE_EVENT = T.let(Object.new.freeze, Object)
9
-
10
- sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
11
- def initialize(index, parse_result, file_path)
8
+ sig do
9
+ params(index: Index, dispatcher: Prism::Dispatcher, parse_result: Prism::ParseResult, file_path: String).void
10
+ end
11
+ def initialize(index, dispatcher, parse_result, file_path)
12
12
  @index = index
13
13
  @file_path = file_path
14
- @stack = T.let([], T::Array[String])
14
+ @visibility_stack = T.let([Entry::Visibility::PUBLIC], T::Array[Entry::Visibility])
15
15
  @comments_by_line = T.let(
16
16
  parse_result.comments.to_h do |c|
17
17
  [c.location.start_line, c]
18
18
  end,
19
19
  T::Hash[Integer, Prism::Comment],
20
20
  )
21
- @queue = T.let([], T::Array[Object])
22
- @current_owner = T.let(nil, T.nilable(Entry::Namespace))
23
-
24
- super()
25
- end
26
-
27
- sig { params(node: Prism::Node).void }
28
- def collect(node)
29
- @queue = [node]
30
-
31
- until @queue.empty?
32
- node_or_event = @queue.shift
33
-
34
- case node_or_event
35
- when Prism::ProgramNode
36
- @queue << node_or_event.statements
37
- when Prism::StatementsNode
38
- T.unsafe(@queue).prepend(*node_or_event.body)
39
- when Prism::ClassNode
40
- add_class_entry(node_or_event)
41
- when Prism::ModuleNode
42
- add_module_entry(node_or_event)
43
- when Prism::MultiWriteNode
44
- handle_multi_write_node(node_or_event)
45
- when Prism::ConstantPathWriteNode
46
- handle_constant_path_write_node(node_or_event)
47
- when Prism::ConstantPathOrWriteNode
48
- handle_constant_path_or_write_node(node_or_event)
49
- when Prism::ConstantPathOperatorWriteNode
50
- handle_constant_path_operator_write_node(node_or_event)
51
- when Prism::ConstantPathAndWriteNode
52
- handle_constant_path_and_write_node(node_or_event)
53
- when Prism::ConstantWriteNode
54
- handle_constant_write_node(node_or_event)
55
- when Prism::ConstantOrWriteNode
56
- name = fully_qualify_name(node_or_event.name.to_s)
57
- add_constant(node_or_event, name)
58
- when Prism::ConstantAndWriteNode
59
- name = fully_qualify_name(node_or_event.name.to_s)
60
- add_constant(node_or_event, name)
61
- when Prism::ConstantOperatorWriteNode
62
- name = fully_qualify_name(node_or_event.name.to_s)
63
- add_constant(node_or_event, name)
64
- when Prism::CallNode
65
- handle_call_node(node_or_event)
66
- when Prism::DefNode
67
- handle_def_node(node_or_event)
68
- when LEAVE_EVENT
69
- @stack.pop
70
- end
21
+ @inside_def = T.let(false, T::Boolean)
22
+
23
+ # The nesting stack we're currently inside. Used to determine the fully qualified name of constants, but only
24
+ # stored by unresolved aliases which need the original nesting to be lazily resolved
25
+ @stack = T.let([], T::Array[String])
26
+
27
+ # A stack of namespace entries that represent where we currently are. Used to properly assign methods to an owner
28
+ @owner_stack = T.let([], T::Array[Entry::Namespace])
29
+
30
+ dispatcher.register(
31
+ self,
32
+ :on_class_node_enter,
33
+ :on_class_node_leave,
34
+ :on_module_node_enter,
35
+ :on_module_node_leave,
36
+ :on_def_node_enter,
37
+ :on_def_node_leave,
38
+ :on_call_node_enter,
39
+ :on_call_node_leave,
40
+ :on_multi_write_node_enter,
41
+ :on_constant_path_write_node_enter,
42
+ :on_constant_path_or_write_node_enter,
43
+ :on_constant_path_operator_write_node_enter,
44
+ :on_constant_path_and_write_node_enter,
45
+ :on_constant_or_write_node_enter,
46
+ :on_constant_write_node_enter,
47
+ :on_constant_or_write_node_enter,
48
+ :on_constant_and_write_node_enter,
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,
55
+ )
56
+ end
57
+
58
+ sig { params(node: Prism::ClassNode).void }
59
+ def on_class_node_enter(node)
60
+ @visibility_stack.push(Entry::Visibility::PUBLIC)
61
+ name = node.constant_path.location.slice
62
+
63
+ comments = collect_comments(node)
64
+
65
+ superclass = node.superclass
66
+ parent_class = case superclass
67
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
68
+ superclass.slice
71
69
  end
70
+
71
+ nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
72
+
73
+ entry = Entry::Class.new(
74
+ nesting,
75
+ @file_path,
76
+ node.location,
77
+ comments,
78
+ parent_class,
79
+ )
80
+
81
+ @owner_stack << entry
82
+ @index << entry
83
+ @stack << name
72
84
  end
73
85
 
74
- private
86
+ sig { params(node: Prism::ClassNode).void }
87
+ def on_class_node_leave(node)
88
+ @stack.pop
89
+ @owner_stack.pop
90
+ @visibility_stack.pop
91
+ end
92
+
93
+ sig { params(node: Prism::ModuleNode).void }
94
+ def on_module_node_enter(node)
95
+ @visibility_stack.push(Entry::Visibility::PUBLIC)
96
+ name = node.constant_path.location.slice
97
+
98
+ comments = collect_comments(node)
99
+
100
+ nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
101
+ entry = Entry::Module.new(nesting, @file_path, node.location, comments)
102
+
103
+ @owner_stack << entry
104
+ @index << entry
105
+ @stack << name
106
+ end
107
+
108
+ sig { params(node: Prism::ModuleNode).void }
109
+ def on_module_node_leave(node)
110
+ @stack.pop
111
+ @owner_stack.pop
112
+ @visibility_stack.pop
113
+ end
75
114
 
76
115
  sig { params(node: Prism::MultiWriteNode).void }
77
- def handle_multi_write_node(node)
116
+ def on_multi_write_node_enter(node)
78
117
  value = node.value
79
118
  values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
80
119
 
@@ -94,7 +133,7 @@ module RubyIndexer
94
133
  end
95
134
 
96
135
  sig { params(node: Prism::ConstantPathWriteNode).void }
97
- def handle_constant_path_write_node(node)
136
+ def on_constant_path_write_node_enter(node)
98
137
  # ignore variable constants like `var::FOO` or `self.class::FOO`
99
138
  target = node.target
100
139
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -104,7 +143,7 @@ module RubyIndexer
104
143
  end
105
144
 
106
145
  sig { params(node: Prism::ConstantPathOrWriteNode).void }
107
- def handle_constant_path_or_write_node(node)
146
+ def on_constant_path_or_write_node_enter(node)
108
147
  # ignore variable constants like `var::FOO` or `self.class::FOO`
109
148
  target = node.target
110
149
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -114,7 +153,7 @@ module RubyIndexer
114
153
  end
115
154
 
116
155
  sig { params(node: Prism::ConstantPathOperatorWriteNode).void }
117
- def handle_constant_path_operator_write_node(node)
156
+ def on_constant_path_operator_write_node_enter(node)
118
157
  # ignore variable constants like `var::FOO` or `self.class::FOO`
119
158
  target = node.target
120
159
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -124,7 +163,7 @@ module RubyIndexer
124
163
  end
125
164
 
126
165
  sig { params(node: Prism::ConstantPathAndWriteNode).void }
127
- def handle_constant_path_and_write_node(node)
166
+ def on_constant_path_and_write_node_enter(node)
128
167
  # ignore variable constants like `var::FOO` or `self.class::FOO`
129
168
  target = node.target
130
169
  return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
@@ -134,13 +173,31 @@ module RubyIndexer
134
173
  end
135
174
 
136
175
  sig { params(node: Prism::ConstantWriteNode).void }
137
- def handle_constant_write_node(node)
176
+ def on_constant_write_node_enter(node)
177
+ name = fully_qualify_name(node.name.to_s)
178
+ add_constant(node, name)
179
+ end
180
+
181
+ sig { params(node: Prism::ConstantOrWriteNode).void }
182
+ def on_constant_or_write_node_enter(node)
183
+ name = fully_qualify_name(node.name.to_s)
184
+ add_constant(node, name)
185
+ end
186
+
187
+ sig { params(node: Prism::ConstantAndWriteNode).void }
188
+ def on_constant_and_write_node_enter(node)
189
+ name = fully_qualify_name(node.name.to_s)
190
+ add_constant(node, name)
191
+ end
192
+
193
+ sig { params(node: Prism::ConstantOperatorWriteNode).void }
194
+ def on_constant_operator_write_node_enter(node)
138
195
  name = fully_qualify_name(node.name.to_s)
139
196
  add_constant(node, name)
140
197
  end
141
198
 
142
199
  sig { params(node: Prism::CallNode).void }
143
- def handle_call_node(node)
200
+ def on_call_node_enter(node)
144
201
  message = node.name
145
202
 
146
203
  case message
@@ -152,17 +209,36 @@ module RubyIndexer
152
209
  handle_attribute(node, reader: false, writer: true)
153
210
  when :attr_accessor
154
211
  handle_attribute(node, reader: true, writer: true)
155
- when :include
156
- handle_include(node)
157
- when :prepend
158
- handle_prepend(node)
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
159
233
  end
160
234
  end
161
235
 
162
236
  sig { params(node: Prism::DefNode).void }
163
- def handle_def_node(node)
237
+ def on_def_node_enter(node)
238
+ @inside_def = true
164
239
  method_name = node.name.to_s
165
240
  comments = collect_comments(node)
241
+
166
242
  case node.receiver
167
243
  when nil
168
244
  @index << Entry::InstanceMethod.new(
@@ -171,7 +247,8 @@ module RubyIndexer
171
247
  node.location,
172
248
  comments,
173
249
  node.parameters,
174
- @current_owner,
250
+ current_visibility,
251
+ @owner_stack.last,
175
252
  )
176
253
  when Prism::SelfNode
177
254
  @index << Entry::SingletonMethod.new(
@@ -180,11 +257,89 @@ module RubyIndexer
180
257
  node.location,
181
258
  comments,
182
259
  node.parameters,
183
- @current_owner,
260
+ current_visibility,
261
+ @owner_stack.last,
184
262
  )
185
263
  end
186
264
  end
187
265
 
266
+ sig { params(node: Prism::DefNode).void }
267
+ def on_def_node_leave(node)
268
+ @inside_def = false
269
+ end
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
+
341
+ private
342
+
188
343
  sig { params(node: Prism::CallNode).void }
189
344
  def handle_private_constant(node)
190
345
  arguments = node.arguments&.arguments
@@ -207,7 +362,7 @@ module RubyIndexer
207
362
  # The private_constant method does not resolve the constant name. It always points to a constant that needs to
208
363
  # exist in the current namespace
209
364
  entries = @index[fully_qualify_name(name)]
210
- entries&.each { |entry| entry.visibility = :private }
365
+ entries&.each { |entry| entry.visibility = Entry::Visibility::PRIVATE }
211
366
  end
212
367
 
213
368
  sig do
@@ -240,62 +395,16 @@ module RubyIndexer
240
395
 
241
396
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
242
397
  # indexed too
243
- @queue.prepend(value)
244
398
  Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
245
399
  when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
246
400
  Prism::ConstantPathAndWriteNode
247
401
 
248
- @queue.prepend(value)
249
402
  Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
250
403
  else
251
404
  Entry::Constant.new(name, @file_path, node.location, comments)
252
405
  end
253
406
  end
254
407
 
255
- sig { params(node: Prism::ModuleNode).void }
256
- def add_module_entry(node)
257
- name = node.constant_path.location.slice
258
- unless /^[A-Z:]/.match?(name)
259
- @queue << node.body
260
- return
261
- end
262
-
263
- comments = collect_comments(node)
264
- @current_owner = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
265
- @index << @current_owner
266
- @stack << name
267
- @queue.prepend(node.body, LEAVE_EVENT)
268
- end
269
-
270
- sig { params(node: Prism::ClassNode).void }
271
- def add_class_entry(node)
272
- name = node.constant_path.location.slice
273
-
274
- unless /^[A-Z:]/.match?(name)
275
- @queue << node.body
276
- return
277
- end
278
-
279
- comments = collect_comments(node)
280
-
281
- superclass = node.superclass
282
- parent_class = case superclass
283
- when Prism::ConstantReadNode, Prism::ConstantPathNode
284
- superclass.slice
285
- end
286
-
287
- @current_owner = Entry::Class.new(
288
- fully_qualify_name(name),
289
- @file_path,
290
- node.location,
291
- comments,
292
- parent_class,
293
- )
294
- @index << @current_owner
295
- @stack << name
296
- @queue.prepend(node.body, LEAVE_EVENT)
297
- end
298
-
299
408
  sig { params(node: Prism::Node).returns(T::Array[String]) }
300
409
  def collect_comments(node)
301
410
  comments = []
@@ -350,39 +459,48 @@ module RubyIndexer
350
459
 
351
460
  next unless name && loc
352
461
 
353
- @index << Entry::Accessor.new(name, @file_path, loc, comments, @current_owner) if reader
354
- @index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @current_owner) 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
355
471
  end
356
472
  end
357
473
 
358
- sig { params(node: Prism::CallNode).void }
359
- def handle_include(node)
360
- handle_module_operation(node, :included_modules)
361
- end
362
-
363
- sig { params(node: Prism::CallNode).void }
364
- def handle_prepend(node)
365
- handle_module_operation(node, :prepended_modules)
366
- end
367
-
368
474
  sig { params(node: Prism::CallNode, operation: Symbol).void }
369
475
  def handle_module_operation(node, operation)
370
- return unless @current_owner
476
+ return if @inside_def
477
+
478
+ owner = @owner_stack.last
479
+ return unless owner
371
480
 
372
481
  arguments = node.arguments&.arguments
373
482
  return unless arguments
374
483
 
375
- names = arguments.filter_map do |node|
376
- if node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
377
- 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)
378
494
  end
379
- rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError
380
- # TO DO: add MissingNodesInConstantPathError when released in Prism
381
- # If a constant path reference is dynamic or missing parts, we can't
382
- # index it
495
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
496
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
497
+ # Do nothing
383
498
  end
384
- collection = operation == :included_modules ? @current_owner.included_modules : @current_owner.prepended_modules
385
- collection.concat(names)
499
+ end
500
+
501
+ sig { returns(Entry::Visibility) }
502
+ def current_visibility
503
+ T.must(@visibility_stack.last)
386
504
  end
387
505
  end
388
506
  end