ruby-lsp 0.12.2 → 0.13.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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +20 -4
- data/exe/ruby-lsp-doctor +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/{visitor.rb → collector.rb} +144 -61
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +9 -4
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -12
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +22 -4
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/index_test.rb +64 -0
- data/lib/ruby_indexer/test/method_test.rb +80 -0
- data/lib/ruby_lsp/addon.rb +9 -13
- data/lib/ruby_lsp/document.rb +7 -9
- data/lib/ruby_lsp/executor.rb +54 -51
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/listener.rb +4 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -4
- data/lib/ruby_lsp/requests/code_lens.rb +16 -7
- data/lib/ruby_lsp/requests/completion.rb +60 -8
- data/lib/ruby_lsp/requests/definition.rb +55 -29
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +20 -11
- data/lib/ruby_lsp/requests/document_link.rb +2 -3
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
- data/lib/ruby_lsp/requests/formatting.rb +0 -5
- data/lib/ruby_lsp/requests/hover.rb +23 -4
- data/lib/ruby_lsp/requests/inlay_hints.rb +42 -4
- data/lib/ruby_lsp/requests/on_type_formatting.rb +18 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +41 -16
- data/lib/ruby_lsp/requests/support/common.rb +22 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
- data/lib/ruby_lsp/requests/workspace_symbol.rb +6 -11
- data/lib/ruby_lsp/ruby_document.rb +14 -0
- data/lib/ruby_lsp/setup_bundler.rb +2 -0
- data/lib/ruby_lsp/store.rb +5 -3
- data/lib/ruby_lsp/utils.rb +8 -3
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6bace664e1540c1333df9dabfb2e7bdd8bda0f910033b47b188e4a82ddacb15
|
4
|
+
data.tar.gz: be9e5da0492b26b5cb6e07074d01d7874104b70286c1578342b9639158fed147
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d8e61433686271a68dafe8db79765ed509a115ab6f06060d7669d279f48218fe2eada2c12e3b45f155d27a41774771681cdab92700d27f426e75ed89287ca03
|
7
|
+
data.tar.gz: 611c050b751050934d0fd93e9e52089fe63fc4d947be75a440dfa0465933814d2682268a10135d036c2cee83fa739156d2b11e42e679ecbed7de62acfee9d621
|
data/README.md
CHANGED
@@ -52,6 +52,7 @@ The Ruby LSP provides an addon system that allows other gems to enhance the base
|
|
52
52
|
features. This is the mechanism that powers addons like
|
53
53
|
|
54
54
|
- [Ruby LSP Rails](https://github.com/Shopify/ruby-lsp-rails)
|
55
|
+
- [Ruby LSP RSpec](https://github.com/st0012/ruby-lsp-rspec)
|
55
56
|
|
56
57
|
For instructions on how to create addons, see the [addons documentation](ADDONS.md).
|
57
58
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.13.0
|
data/exe/ruby-lsp-check
CHANGED
@@ -17,8 +17,6 @@ end
|
|
17
17
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
18
18
|
require "ruby_lsp/internal"
|
19
19
|
|
20
|
-
RubyLsp::Addon.load_addons
|
21
|
-
|
22
20
|
T::Utils.run_all_sig_blocks
|
23
21
|
|
24
22
|
files = Dir.glob("#{Dir.pwd}/**/*.rb")
|
@@ -28,6 +26,7 @@ puts "Verifying that all automatic LSP requests execute successfully. This may t
|
|
28
26
|
errors = {}
|
29
27
|
store = RubyLsp::Store.new
|
30
28
|
message_queue = Thread::Queue.new
|
29
|
+
RubyLsp::Addon.load_addons(message_queue)
|
31
30
|
executor = RubyLsp::Executor.new(store, message_queue)
|
32
31
|
|
33
32
|
files.each_with_index do |file, index|
|
@@ -50,13 +49,30 @@ end
|
|
50
49
|
puts "\n"
|
51
50
|
message_queue.close
|
52
51
|
|
52
|
+
# Indexing
|
53
|
+
puts "Verifying that indexing executes successfully. This may take a while..."
|
54
|
+
|
55
|
+
index = RubyIndexer::Index.new
|
56
|
+
indexables = RubyIndexer.configuration.indexables
|
57
|
+
|
58
|
+
indexables.each_with_index do |indexable, i|
|
59
|
+
result = Prism.parse(File.read(indexable.full_path))
|
60
|
+
collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
|
61
|
+
collector.collect(result.value)
|
62
|
+
rescue => e
|
63
|
+
errors[indexable.full_path] = e
|
64
|
+
ensure
|
65
|
+
print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"]
|
66
|
+
end
|
67
|
+
puts "\n"
|
68
|
+
|
53
69
|
if errors.empty?
|
54
|
-
puts "All
|
70
|
+
puts "All operations completed successfully!"
|
55
71
|
exit
|
56
72
|
end
|
57
73
|
|
58
74
|
puts <<~ERRORS
|
59
|
-
Errors while executing
|
75
|
+
Errors while executing:
|
60
76
|
|
61
77
|
#{errors.map { |file, error| "#{file}: #{error.message}" }.join("\n")}
|
62
78
|
ERRORS
|
data/exe/ruby-lsp-doctor
CHANGED
@@ -10,6 +10,6 @@ RubyIndexer.configuration.indexables.each do |indexable|
|
|
10
10
|
puts "indexing: #{indexable.full_path}"
|
11
11
|
content = File.read(indexable.full_path)
|
12
12
|
result = Prism.parse(content)
|
13
|
-
|
14
|
-
result.value
|
13
|
+
collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
|
14
|
+
collector.collect(result.value)
|
15
15
|
end
|
@@ -2,9 +2,11 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyIndexer
|
5
|
-
class
|
5
|
+
class Collector
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
LEAVE_EVENT = T.let(Object.new.freeze, Object)
|
9
|
+
|
8
10
|
sig { params(index: Index, parse_result: Prism::ParseResult, file_path: String).void }
|
9
11
|
def initialize(index, parse_result, file_path)
|
10
12
|
@index = index
|
@@ -16,26 +18,67 @@ module RubyIndexer
|
|
16
18
|
end,
|
17
19
|
T::Hash[Integer, Prism::Comment],
|
18
20
|
)
|
21
|
+
@queue = T.let([], T::Array[Object])
|
22
|
+
@current_owner = T.let(nil, T.nilable(Entry::Namespace))
|
19
23
|
|
20
24
|
super()
|
21
25
|
end
|
22
26
|
|
23
|
-
sig {
|
24
|
-
def
|
25
|
-
|
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
|
71
|
+
end
|
26
72
|
end
|
27
73
|
|
28
|
-
|
29
|
-
def visit_module_node(node)
|
30
|
-
add_module_entry(node)
|
31
|
-
end
|
74
|
+
private
|
32
75
|
|
33
|
-
sig {
|
34
|
-
def
|
76
|
+
sig { params(node: Prism::MultiWriteNode).void }
|
77
|
+
def handle_multi_write_node(node)
|
35
78
|
value = node.value
|
36
79
|
values = value.is_a?(Prism::ArrayNode) && value.opening_loc ? value.elements : []
|
37
80
|
|
38
|
-
node.
|
81
|
+
[*node.lefts, *node.rest, *node.rights].each_with_index do |target, i|
|
39
82
|
current_value = values[i]
|
40
83
|
# The moment we find a splat on the right hand side of the assignment, we can no longer figure out which value
|
41
84
|
# gets assigned to what
|
@@ -50,8 +93,8 @@ module RubyIndexer
|
|
50
93
|
end
|
51
94
|
end
|
52
95
|
|
53
|
-
sig {
|
54
|
-
def
|
96
|
+
sig { params(node: Prism::ConstantPathWriteNode).void }
|
97
|
+
def handle_constant_path_write_node(node)
|
55
98
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
56
99
|
target = node.target
|
57
100
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -60,8 +103,8 @@ module RubyIndexer
|
|
60
103
|
add_constant(node, name)
|
61
104
|
end
|
62
105
|
|
63
|
-
sig {
|
64
|
-
def
|
106
|
+
sig { params(node: Prism::ConstantPathOrWriteNode).void }
|
107
|
+
def handle_constant_path_or_write_node(node)
|
65
108
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
66
109
|
target = node.target
|
67
110
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -70,8 +113,8 @@ module RubyIndexer
|
|
70
113
|
add_constant(node, name)
|
71
114
|
end
|
72
115
|
|
73
|
-
sig {
|
74
|
-
def
|
116
|
+
sig { params(node: Prism::ConstantPathOperatorWriteNode).void }
|
117
|
+
def handle_constant_path_operator_write_node(node)
|
75
118
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
76
119
|
target = node.target
|
77
120
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -80,8 +123,8 @@ module RubyIndexer
|
|
80
123
|
add_constant(node, name)
|
81
124
|
end
|
82
125
|
|
83
|
-
sig {
|
84
|
-
def
|
126
|
+
sig { params(node: Prism::ConstantPathAndWriteNode).void }
|
127
|
+
def handle_constant_path_and_write_node(node)
|
85
128
|
# ignore variable constants like `var::FOO` or `self.class::FOO`
|
86
129
|
target = node.target
|
87
130
|
return unless target.parent.nil? || target.parent.is_a?(Prism::ConstantReadNode)
|
@@ -90,50 +133,54 @@ module RubyIndexer
|
|
90
133
|
add_constant(node, name)
|
91
134
|
end
|
92
135
|
|
93
|
-
sig {
|
94
|
-
def
|
136
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
137
|
+
def handle_constant_write_node(node)
|
95
138
|
name = fully_qualify_name(node.name.to_s)
|
96
139
|
add_constant(node, name)
|
97
140
|
end
|
98
141
|
|
99
|
-
sig {
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
name = fully_qualify_name(node.name.to_s)
|
114
|
-
add_constant(node, name)
|
115
|
-
end
|
116
|
-
|
117
|
-
sig { override.params(node: Prism::CallNode).void }
|
118
|
-
def visit_call_node(node)
|
119
|
-
message = node.message
|
120
|
-
handle_private_constant(node) if message == "private_constant"
|
142
|
+
sig { params(node: Prism::CallNode).void }
|
143
|
+
def handle_call_node(node)
|
144
|
+
message = node.name
|
145
|
+
|
146
|
+
case message
|
147
|
+
when :private_constant
|
148
|
+
handle_private_constant(node)
|
149
|
+
when :attr_reader
|
150
|
+
handle_attribute(node, reader: true, writer: false)
|
151
|
+
when :attr_writer
|
152
|
+
handle_attribute(node, reader: false, writer: true)
|
153
|
+
when :attr_accessor
|
154
|
+
handle_attribute(node, reader: true, writer: true)
|
155
|
+
end
|
121
156
|
end
|
122
157
|
|
123
|
-
sig {
|
124
|
-
def
|
158
|
+
sig { params(node: Prism::DefNode).void }
|
159
|
+
def handle_def_node(node)
|
125
160
|
method_name = node.name.to_s
|
126
161
|
comments = collect_comments(node)
|
127
162
|
case node.receiver
|
128
163
|
when nil
|
129
|
-
@index << Entry::InstanceMethod.new(
|
164
|
+
@index << Entry::InstanceMethod.new(
|
165
|
+
method_name,
|
166
|
+
@file_path,
|
167
|
+
node.location,
|
168
|
+
comments,
|
169
|
+
node.parameters,
|
170
|
+
@current_owner,
|
171
|
+
)
|
130
172
|
when Prism::SelfNode
|
131
|
-
@index << Entry::SingletonMethod.new(
|
173
|
+
@index << Entry::SingletonMethod.new(
|
174
|
+
method_name,
|
175
|
+
@file_path,
|
176
|
+
node.location,
|
177
|
+
comments,
|
178
|
+
node.parameters,
|
179
|
+
@current_owner,
|
180
|
+
)
|
132
181
|
end
|
133
182
|
end
|
134
183
|
|
135
|
-
private
|
136
|
-
|
137
184
|
sig { params(node: Prism::CallNode).void }
|
138
185
|
def handle_private_constant(node)
|
139
186
|
arguments = node.arguments&.arguments
|
@@ -189,12 +236,12 @@ module RubyIndexer
|
|
189
236
|
|
190
237
|
# If the right hand side is another constant assignment, we need to visit it because that constant has to be
|
191
238
|
# indexed too
|
192
|
-
|
239
|
+
@queue.prepend(value)
|
193
240
|
Entry::UnresolvedAlias.new(value.name.to_s, @stack.dup, name, @file_path, node.location, comments)
|
194
241
|
when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
195
242
|
Prism::ConstantPathAndWriteNode
|
196
243
|
|
197
|
-
|
244
|
+
@queue.prepend(value)
|
198
245
|
Entry::UnresolvedAlias.new(value.target.slice, @stack.dup, name, @file_path, node.location, comments)
|
199
246
|
else
|
200
247
|
Entry::Constant.new(name, @file_path, node.location, comments)
|
@@ -204,20 +251,26 @@ module RubyIndexer
|
|
204
251
|
sig { params(node: Prism::ModuleNode).void }
|
205
252
|
def add_module_entry(node)
|
206
253
|
name = node.constant_path.location.slice
|
207
|
-
|
254
|
+
unless /^[A-Z:]/.match?(name)
|
255
|
+
@queue << node.body
|
256
|
+
return
|
257
|
+
end
|
208
258
|
|
209
259
|
comments = collect_comments(node)
|
210
|
-
|
211
|
-
@index <<
|
260
|
+
@current_owner = Entry::Module.new(fully_qualify_name(name), @file_path, node.location, comments)
|
261
|
+
@index << @current_owner
|
212
262
|
@stack << name
|
213
|
-
|
214
|
-
@stack.pop
|
263
|
+
@queue.prepend(node.body, LEAVE_EVENT)
|
215
264
|
end
|
216
265
|
|
217
266
|
sig { params(node: Prism::ClassNode).void }
|
218
267
|
def add_class_entry(node)
|
219
268
|
name = node.constant_path.location.slice
|
220
|
-
|
269
|
+
|
270
|
+
unless /^[A-Z:]/.match?(name)
|
271
|
+
@queue << node.body
|
272
|
+
return
|
273
|
+
end
|
221
274
|
|
222
275
|
comments = collect_comments(node)
|
223
276
|
|
@@ -227,10 +280,16 @@ module RubyIndexer
|
|
227
280
|
superclass.slice
|
228
281
|
end
|
229
282
|
|
230
|
-
@
|
283
|
+
@current_owner = Entry::Class.new(
|
284
|
+
fully_qualify_name(name),
|
285
|
+
@file_path,
|
286
|
+
node.location,
|
287
|
+
comments,
|
288
|
+
parent_class,
|
289
|
+
)
|
290
|
+
@index << @current_owner
|
231
291
|
@stack << name
|
232
|
-
|
233
|
-
@stack.pop
|
292
|
+
@queue.prepend(node.body, LEAVE_EVENT)
|
234
293
|
end
|
235
294
|
|
236
295
|
sig { params(node: Prism::Node).returns(T::Array[String]) }
|
@@ -249,7 +308,7 @@ module RubyIndexer
|
|
249
308
|
|
250
309
|
comment_content.delete_prefix!("#")
|
251
310
|
comment_content.delete_prefix!(" ")
|
252
|
-
comments.
|
311
|
+
comments.prepend(comment_content)
|
253
312
|
end
|
254
313
|
|
255
314
|
comments
|
@@ -263,5 +322,29 @@ module RubyIndexer
|
|
263
322
|
"#{@stack.join("::")}::#{name}"
|
264
323
|
end.delete_prefix("::")
|
265
324
|
end
|
325
|
+
|
326
|
+
sig { params(node: Prism::CallNode, reader: T::Boolean, writer: T::Boolean).void }
|
327
|
+
def handle_attribute(node, reader:, writer:)
|
328
|
+
arguments = node.arguments&.arguments
|
329
|
+
return unless arguments
|
330
|
+
|
331
|
+
receiver = node.receiver
|
332
|
+
return unless receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
333
|
+
|
334
|
+
comments = collect_comments(node)
|
335
|
+
arguments.each do |argument|
|
336
|
+
name, loc = case argument
|
337
|
+
when Prism::SymbolNode
|
338
|
+
[argument.value, argument.value_loc]
|
339
|
+
when Prism::StringNode
|
340
|
+
[argument.content, argument.content_loc]
|
341
|
+
end
|
342
|
+
|
343
|
+
next unless name && loc
|
344
|
+
|
345
|
+
@index << Entry::Accessor.new(name, @file_path, loc, comments, @current_owner) if reader
|
346
|
+
@index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @current_owner) if writer
|
347
|
+
end
|
348
|
+
end
|
266
349
|
end
|
267
350
|
end
|
@@ -71,8 +71,13 @@ module RubyIndexer
|
|
71
71
|
|
72
72
|
Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
|
73
73
|
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
|
74
|
-
# entry is expensive, we memoize it
|
75
|
-
|
74
|
+
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
|
75
|
+
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
|
76
|
+
# each one of them belongs to a different $LOAD_PATH entry
|
77
|
+
if load_path_entry.nil? || !path.start_with?(load_path_entry)
|
78
|
+
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
|
79
|
+
end
|
80
|
+
|
76
81
|
IndexablePath.new(load_path_entry, path)
|
77
82
|
end
|
78
83
|
end
|
@@ -115,7 +120,7 @@ module RubyIndexer
|
|
115
120
|
IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
|
116
121
|
end,
|
117
122
|
)
|
118
|
-
|
123
|
+
elsif pathname.extname == ".rb"
|
119
124
|
# If the default_path is a Ruby file, we index it
|
120
125
|
indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
|
121
126
|
end
|
@@ -144,7 +149,7 @@ module RubyIndexer
|
|
144
149
|
# just ignore if they're missing
|
145
150
|
end
|
146
151
|
|
147
|
-
indexables.uniq!
|
152
|
+
indexables.uniq!(&:full_path)
|
148
153
|
indexables
|
149
154
|
end
|
150
155
|
|
@@ -90,15 +90,67 @@ module RubyIndexer
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
+
# A required method parameter, e.g. `def foo(a)`
|
93
94
|
class RequiredParameter < Parameter
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
+
# An optional method parameter, e.g. `def foo(a = 123)`
|
98
|
+
class OptionalParameter < Parameter
|
99
|
+
end
|
100
|
+
|
101
|
+
# An required keyword method parameter, e.g. `def foo(a:)`
|
102
|
+
class KeywordParameter < Parameter
|
103
|
+
end
|
104
|
+
|
105
|
+
# An optional keyword method parameter, e.g. `def foo(a: 123)`
|
106
|
+
class OptionalKeywordParameter < Parameter
|
107
|
+
end
|
108
|
+
|
109
|
+
class Member < Entry
|
110
|
+
extend T::Sig
|
111
|
+
extend T::Helpers
|
112
|
+
|
113
|
+
abstract!
|
114
|
+
|
115
|
+
sig { returns(T.nilable(Entry::Namespace)) }
|
116
|
+
attr_reader :owner
|
117
|
+
|
118
|
+
sig do
|
119
|
+
params(
|
120
|
+
name: String,
|
121
|
+
file_path: String,
|
122
|
+
location: Prism::Location,
|
123
|
+
comments: T::Array[String],
|
124
|
+
owner: T.nilable(Entry::Namespace),
|
125
|
+
).void
|
126
|
+
end
|
127
|
+
def initialize(name, file_path, location, comments, owner)
|
128
|
+
super(name, file_path, location, comments)
|
129
|
+
@owner = owner
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { abstract.returns(T::Array[Parameter]) }
|
133
|
+
def parameters; end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Accessor < Member
|
137
|
+
extend T::Sig
|
138
|
+
|
139
|
+
sig { override.returns(T::Array[Parameter]) }
|
140
|
+
def parameters
|
141
|
+
params = []
|
142
|
+
params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
|
143
|
+
params
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Method < Member
|
97
148
|
extend T::Sig
|
98
149
|
extend T::Helpers
|
150
|
+
|
99
151
|
abstract!
|
100
152
|
|
101
|
-
sig { returns(T::Array[Parameter]) }
|
153
|
+
sig { override.returns(T::Array[Parameter]) }
|
102
154
|
attr_reader :parameters
|
103
155
|
|
104
156
|
sig do
|
@@ -108,10 +160,12 @@ module RubyIndexer
|
|
108
160
|
location: Prism::Location,
|
109
161
|
comments: T::Array[String],
|
110
162
|
parameters_node: T.nilable(Prism::ParametersNode),
|
163
|
+
owner: T.nilable(Entry::Namespace),
|
111
164
|
).void
|
112
165
|
end
|
113
|
-
def initialize(name, file_path, location, comments, parameters_node)
|
114
|
-
super(name, file_path, location, comments)
|
166
|
+
def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
|
167
|
+
super(name, file_path, location, comments, owner)
|
168
|
+
|
115
169
|
@parameters = T.let(list_params(parameters_node), T::Array[Parameter])
|
116
170
|
end
|
117
171
|
|
@@ -121,23 +175,46 @@ module RubyIndexer
|
|
121
175
|
def list_params(parameters_node)
|
122
176
|
return [] unless parameters_node
|
123
177
|
|
124
|
-
|
178
|
+
parameters = []
|
179
|
+
|
180
|
+
parameters_node.requireds.each do |required|
|
125
181
|
name = parameter_name(required)
|
126
182
|
next unless name
|
127
183
|
|
128
|
-
RequiredParameter.new(name: name)
|
184
|
+
parameters << RequiredParameter.new(name: name)
|
129
185
|
end
|
130
|
-
end
|
131
186
|
|
132
|
-
|
133
|
-
|
187
|
+
parameters_node.optionals.each do |optional|
|
188
|
+
name = parameter_name(optional)
|
189
|
+
next unless name
|
190
|
+
|
191
|
+
parameters << OptionalParameter.new(name: name)
|
192
|
+
end
|
193
|
+
|
194
|
+
parameters_node.keywords.each do |keyword|
|
195
|
+
name = parameter_name(keyword)
|
196
|
+
next unless name
|
197
|
+
|
198
|
+
case keyword
|
199
|
+
when Prism::RequiredKeywordParameterNode
|
200
|
+
parameters << KeywordParameter.new(name: name)
|
201
|
+
when Prism::OptionalKeywordParameterNode
|
202
|
+
parameters << OptionalKeywordParameter.new(name: name)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
parameters
|
134
207
|
end
|
208
|
+
|
209
|
+
sig { params(node: Prism::Node).returns(T.nilable(Symbol)) }
|
135
210
|
def parameter_name(node)
|
136
211
|
case node
|
137
|
-
when Prism::RequiredParameterNode
|
212
|
+
when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
|
213
|
+
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode
|
138
214
|
node.name
|
139
|
-
when Prism::
|
140
|
-
names = node.
|
215
|
+
when Prism::MultiTargetNode
|
216
|
+
names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
|
217
|
+
|
141
218
|
names_with_commas = names.join(", ")
|
142
219
|
:"(#{names_with_commas})"
|
143
220
|
end
|
@@ -93,8 +93,14 @@ module RubyIndexer
|
|
93
93
|
# [#<Entry::Class name="Foo::Baz">],
|
94
94
|
# ]
|
95
95
|
# ```
|
96
|
-
sig { params(query: String, nesting: T::Array[String]).returns(T::Array[T::Array[Entry]]) }
|
97
|
-
def prefix_search(query, nesting)
|
96
|
+
sig { params(query: String, nesting: T.nilable(T::Array[String])).returns(T::Array[T::Array[Entry]]) }
|
97
|
+
def prefix_search(query, nesting = nil)
|
98
|
+
unless nesting
|
99
|
+
results = @entries_tree.search(query)
|
100
|
+
results.uniq!
|
101
|
+
return results
|
102
|
+
end
|
103
|
+
|
98
104
|
results = nesting.length.downto(0).flat_map do |i|
|
99
105
|
prefix = T.must(nesting[0...i]).join("::")
|
100
106
|
namespaced_query = prefix.empty? ? query : "#{prefix}::#{query}"
|
@@ -180,8 +186,8 @@ module RubyIndexer
|
|
180
186
|
def index_single(indexable_path, source = nil)
|
181
187
|
content = source || File.read(indexable_path.full_path)
|
182
188
|
result = Prism.parse(content)
|
183
|
-
|
184
|
-
result.value
|
189
|
+
collector = Collector.new(self, result, indexable_path.full_path)
|
190
|
+
collector.collect(result.value)
|
185
191
|
|
186
192
|
require_path = indexable_path.require_path
|
187
193
|
@require_paths_tree.insert(require_path, indexable_path) if require_path
|
@@ -231,6 +237,18 @@ module RubyIndexer
|
|
231
237
|
real_parts.join("::")
|
232
238
|
end
|
233
239
|
|
240
|
+
# Attempts to find a given method for a resolved fully qualified receiver name. Returns `nil` if the method does not
|
241
|
+
# exist on that receiver
|
242
|
+
sig { params(method_name: String, receiver_name: String).returns(T.nilable(Entry::Method)) }
|
243
|
+
def resolve_method(method_name, receiver_name)
|
244
|
+
method_entries = T.cast(self[method_name], T.nilable(T::Array[Entry::Method]))
|
245
|
+
owner_entries = self[receiver_name]
|
246
|
+
return unless owner_entries && method_entries
|
247
|
+
|
248
|
+
owner_name = T.must(owner_entries.first).name
|
249
|
+
method_entries.find { |entry| entry.owner&.name == owner_name }
|
250
|
+
end
|
251
|
+
|
234
252
|
private
|
235
253
|
|
236
254
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
@@ -5,7 +5,7 @@ require "yaml"
|
|
5
5
|
require "did_you_mean"
|
6
6
|
|
7
7
|
require "ruby_indexer/lib/ruby_indexer/indexable_path"
|
8
|
-
require "ruby_indexer/lib/ruby_indexer/
|
8
|
+
require "ruby_indexer/lib/ruby_indexer/collector"
|
9
9
|
require "ruby_indexer/lib/ruby_indexer/index"
|
10
10
|
require "ruby_indexer/lib/ruby_indexer/entry"
|
11
11
|
require "ruby_indexer/lib/ruby_indexer/configuration"
|
@@ -84,6 +84,16 @@ module RubyIndexer
|
|
84
84
|
)
|
85
85
|
end
|
86
86
|
|
87
|
+
def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir
|
88
|
+
path = Pathname.new(RbConfig::CONFIG["rubylibdir"]).join("extra_file.txt").to_s
|
89
|
+
FileUtils.touch(path)
|
90
|
+
indexables = @config.indexables
|
91
|
+
|
92
|
+
assert(indexables.none? { |indexable| indexable.full_path == path })
|
93
|
+
ensure
|
94
|
+
FileUtils.rm(T.must(path))
|
95
|
+
end
|
96
|
+
|
87
97
|
def test_paths_are_unique
|
88
98
|
@config.load_config
|
89
99
|
indexables = @config.indexables
|