rucoa 0.9.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -0
  3. data/Gemfile.lock +8 -8
  4. data/data/definitions_ruby_3_1 +0 -0
  5. data/lib/rucoa/configuration.rb +4 -1
  6. data/lib/rucoa/definition_store.rb +350 -105
  7. data/lib/rucoa/definitions/base.rb +14 -3
  8. data/lib/rucoa/definitions/class_definition.rb +14 -64
  9. data/lib/rucoa/definitions/constant_definition.rb +31 -19
  10. data/lib/rucoa/definitions/method_definition.rb +44 -52
  11. data/lib/rucoa/definitions/method_parameter_definition.rb +4 -1
  12. data/lib/rucoa/definitions/module_definition.rb +53 -0
  13. data/lib/rucoa/handler_concerns/diagnostics_publishable.rb +14 -3
  14. data/lib/rucoa/handlers/base.rb +12 -3
  15. data/lib/rucoa/handlers/text_document_definition_handler.rb +3 -99
  16. data/lib/rucoa/handlers/text_document_hover_handler.rb +21 -13
  17. data/lib/rucoa/handlers/text_document_selection_range_handler.rb +8 -2
  18. data/lib/rucoa/location.rb +37 -0
  19. data/lib/rucoa/node_concerns/body.rb +24 -0
  20. data/lib/rucoa/node_concerns/{name_fully_qualifiable.rb → qualified_name.rb} +2 -2
  21. data/lib/rucoa/node_concerns.rb +2 -1
  22. data/lib/rucoa/node_inspector.rb +29 -23
  23. data/lib/rucoa/nodes/base.rb +22 -6
  24. data/lib/rucoa/nodes/begin_node.rb +8 -0
  25. data/lib/rucoa/nodes/casgn_node.rb +1 -1
  26. data/lib/rucoa/nodes/class_node.rb +2 -1
  27. data/lib/rucoa/nodes/const_node.rb +33 -15
  28. data/lib/rucoa/nodes/def_node.rb +2 -2
  29. data/lib/rucoa/nodes/defs_node.rb +1 -1
  30. data/lib/rucoa/nodes/module_node.rb +2 -1
  31. data/lib/rucoa/nodes.rb +2 -1
  32. data/lib/rucoa/parser.rb +14 -14
  33. data/lib/rucoa/parser_builder.rb +6 -1
  34. data/lib/rucoa/position.rb +10 -1
  35. data/lib/rucoa/range.rb +11 -1
  36. data/lib/rucoa/rbs/class_definition_mapper.rb +14 -28
  37. data/lib/rucoa/rbs/constant_definition_mapper.rb +12 -6
  38. data/lib/rucoa/rbs/method_definition_mapper.rb +12 -6
  39. data/lib/rucoa/rbs/module_definition_mapper.rb +39 -6
  40. data/lib/rucoa/rubocop/investigator.rb +4 -1
  41. data/lib/rucoa/server.rb +9 -3
  42. data/lib/rucoa/source.rb +19 -4
  43. data/lib/rucoa/unqualified_name.rb +9 -0
  44. data/lib/rucoa/version.rb +1 -1
  45. data/lib/rucoa/yard/definition_generators/attribute_reader_definition_generator.rb +76 -0
  46. data/lib/rucoa/yard/definition_generators/attribute_writer_definition_generator.rb +76 -0
  47. data/lib/rucoa/yard/definition_generators/base.rb +117 -0
  48. data/lib/rucoa/yard/definition_generators/class_definition_generator.rb +67 -0
  49. data/lib/rucoa/yard/definition_generators/constant_assignment_definition_generator.rb +29 -0
  50. data/lib/rucoa/yard/definition_generators/method_definition_generator.rb +60 -0
  51. data/lib/rucoa/yard/definition_generators/module_definition_generator.rb +68 -0
  52. data/lib/rucoa/yard/definition_generators.rb +15 -0
  53. data/lib/rucoa/yard/definitions_loader.rb +1 -271
  54. data/lib/rucoa/yard.rb +1 -0
  55. data/lib/rucoa.rb +4 -2
  56. metadata +15 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 237fa68185426d66437c43fc2a3f34f7f85ee7309eb4ed6bb6ce4e31b5dfbb6e
4
- data.tar.gz: e9f96daf65c7ccef564b2e06756497fd4d98bd946911487ca2fdd32740436e4b
3
+ metadata.gz: cec8d0284ecf97b03cd43de02d36c16263910dba3c5a1052f73df0bf0fd51f68
4
+ data.tar.gz: b83c8aad102fbd510a0f7474394ab8cf91cd17976bbc365e188c276789815e9a
5
5
  SHA512:
6
- metadata.gz: 3698e9db246d46ce64314df9b4a7418278a6e9f65cc2e80dfece624b547c5ab238af42b456370d1096e872f6e30aefdb132b5b2bd435ec03d1e6cccdac0a908a
7
- data.tar.gz: 5088d4dcc1fa5ad6e053a155246da87602071fa8283b4f42e1b4e9040b222be5f82622343ad2467b5cdf8003b02293e70ea8d391dc99f478ee36a1389311a9d4
6
+ metadata.gz: 3042168fc7a78f71854f96d8367f9f4e1e0c62d4300b4feef36b6596e602b3c40c5150d1d6f0902ab5d7b1015179cdf10c2d1e6beabe68cbf3fa1114f6cdb00e
7
+ data.tar.gz: 6721bdccd41cb883d6b6ab97a5a990b878e088a7832ccb6c42bcc642ff13f1ab3126d8893a43b47a29295caa0ff14ed4b59bdf5ab0ca95c14a4536dee11eb899
data/.rubocop.yml CHANGED
@@ -32,9 +32,18 @@ RSpec/NamedSubject:
32
32
  Security/MarshalLoad:
33
33
  Enabled: false
34
34
 
35
+ Sevencop/AutoloadOrdered:
36
+ Enabled: true
37
+
35
38
  Sevencop/HashLiteralOrder:
36
39
  Enabled: true
37
40
 
41
+ Sevencop/MethodDefinitionKeywordArgumentsOrdered:
42
+ Enabled: true
43
+
44
+ Sevencop/MethodDefinitionMultilineArguments:
45
+ Enabled: true
46
+
38
47
  Style/Documentation:
39
48
  Enabled: false
40
49
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rucoa (0.9.0)
4
+ rucoa (0.11.0)
5
5
  parser
6
6
  rbs
7
7
  rubocop
@@ -27,13 +27,13 @@ GEM
27
27
  rspec-mocks (~> 3.11.0)
28
28
  rspec-core (3.11.0)
29
29
  rspec-support (~> 3.11.0)
30
- rspec-expectations (3.11.0)
30
+ rspec-expectations (3.11.1)
31
31
  diff-lcs (>= 1.2.0, < 2.0)
32
32
  rspec-support (~> 3.11.0)
33
33
  rspec-mocks (3.11.1)
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
35
  rspec-support (~> 3.11.0)
36
- rspec-support (3.11.0)
36
+ rspec-support (3.11.1)
37
37
  rubocop (1.36.0)
38
38
  json (~> 2.3)
39
39
  parallel (~> 1.10)
@@ -46,17 +46,17 @@ GEM
46
46
  unicode-display_width (>= 1.4.0, < 3.0)
47
47
  rubocop-ast (1.21.0)
48
48
  parser (>= 3.1.1.0)
49
- rubocop-performance (1.14.3)
49
+ rubocop-performance (1.15.0)
50
50
  rubocop (>= 1.7.0, < 2.0)
51
51
  rubocop-ast (>= 0.4.0)
52
52
  rubocop-rake (0.6.0)
53
53
  rubocop (~> 1.0)
54
- rubocop-rspec (2.12.1)
55
- rubocop (~> 1.31)
54
+ rubocop-rspec (2.13.2)
55
+ rubocop (~> 1.33)
56
56
  ruby-progressbar (1.11.0)
57
- sevencop (0.10.0)
57
+ sevencop (0.13.0)
58
58
  rubocop
59
- unicode-display_width (2.2.0)
59
+ unicode-display_width (2.3.0)
60
60
  webrick (1.7.0)
61
61
  yard (0.9.28)
62
62
  webrick (~> 1.7.0)
Binary file
@@ -148,7 +148,10 @@ module Rucoa
148
148
  # @param keys [Array<String>]
149
149
  # @param default [Object]
150
150
  # @return [Object]
151
- def fetch(*keys, default:)
151
+ def fetch(
152
+ *keys,
153
+ default:
154
+ )
152
155
  value = @settings.dig(*keys)
153
156
  if value.nil?
154
157
  default
@@ -3,22 +3,29 @@
3
3
  module Rucoa
4
4
  class DefinitionStore
5
5
  def initialize
6
- @definition_by_full_qualified_name = {}
7
- @fully_qualified_names_by_uri = ::Hash.new { |hash, key| hash[key] = [] }
6
+ @definition_by_qualified_name = {}
7
+ @qualified_names_by_uri = ::Hash.new { |hash, key| hash[key] = [] }
8
+ end
9
+
10
+ # @return [String]
11
+ def inspect
12
+ "#<#{self.class} definitions_count=#{@definition_by_qualified_name.count}>"
8
13
  end
9
14
 
10
15
  # @param definitions [Array<Rucoa::Definition::Base>]
11
16
  # @return [void]
12
17
  def bulk_add(definitions)
13
- definitions.each do |definition|
14
- @fully_qualified_names_by_uri["file://#{definition.source_path}"] << definition.fully_qualified_name
15
- @definition_by_full_qualified_name[definition.fully_qualified_name] = definition
18
+ definitions.group_by(&:qualified_name).each_value.map do |grouped_definitions|
19
+ grouped_definitions.reduce(:merge!)
20
+ end.each do |definition|
21
+ @qualified_names_by_uri[definition.location.uri] << definition.qualified_name if definition.location
22
+ @definition_by_qualified_name[definition.qualified_name] = definition
16
23
  end
17
24
  end
18
25
 
19
26
  # @param source [Rucoa::Source]
20
27
  # @return [void]
21
- # @example resolves super class name correctly by using existent definitions
28
+ # @example resolves super class name from definitions
22
29
  # definition_store = Rucoa::DefinitionStore.new
23
30
  # foo = Rucoa::Source.new(
24
31
  # content: <<~RUBY,
@@ -40,115 +47,297 @@ module Rucoa
40
47
  # uri: 'file:///path/to/a/bar.rb',
41
48
  # )
42
49
  # definition_store.update_from(bar)
43
- # definition = definition_store.find_definition_by_fully_qualified_name('A::Bar')
44
- # expect(definition.super_class_fully_qualified_name).to eq('A::Foo')
50
+ # definition = definition_store.find_definition_by_qualified_name('A::Bar')
51
+ # expect(definition.super_class_qualified_name).to eq('A::Foo')
52
+ # @example resolves included module names from definitions
53
+ # definition_store = Rucoa::DefinitionStore.new
54
+ # foo = Rucoa::Source.new(
55
+ # content: <<~RUBY,
56
+ # module A
57
+ # module Foo
58
+ # end
59
+ # end
60
+ # RUBY
61
+ # uri: 'file:///path/to/a/foo.rb',
62
+ # )
63
+ # definition_store.update_from(foo)
64
+ # bar = Rucoa::Source.new(
65
+ # content: <<~RUBY,
66
+ # module A
67
+ # class Bar
68
+ # include Foo
69
+ # end
70
+ # end
71
+ # RUBY
72
+ # uri: 'file:///path/to/a/bar.rb',
73
+ # )
74
+ # definition_store.update_from(bar)
75
+ # definition = definition_store.find_definition_by_qualified_name('A::Bar')
76
+ # expect(definition.included_module_qualified_names).to eq(%w[A::Foo])
45
77
  def update_from(source)
46
- delete_definitions_about(source)
47
-
48
- # Need to store definitions before super class resolution.
49
- source.definitions.group_by(&:source_path).each do |source_path, definitions|
50
- @fully_qualified_names_by_uri["file://#{source_path}"] += definitions.map(&:fully_qualified_name)
51
- definitions.each do |definition|
52
- @definition_by_full_qualified_name[definition.fully_qualified_name] = definition
53
- end
54
- end
55
-
56
- source.definitions.each do |definition|
57
- next unless definition.is_a?(Definitions::ClassDefinition)
58
- next if definition.super_class_resolved?
59
-
60
- definition.super_class_fully_qualified_name = resolve_super_class_of(definition)
61
- end
78
+ delete_definitions_in(source)
79
+ add_definitions_in(source)
80
+ resolve_constants_in(source)
62
81
  end
63
82
 
64
- # @param fully_qualified_name [String]
83
+ # @param qualified_name [String]
65
84
  # @return [Rucoa::Definitions::Base, nil]
66
- def find_definition_by_fully_qualified_name(fully_qualified_name)
67
- @definition_by_full_qualified_name[fully_qualified_name]
85
+ def find_definition_by_qualified_name(qualified_name)
86
+ @definition_by_qualified_name[qualified_name]
68
87
  end
69
88
 
70
89
  # @param method_name [String]
71
90
  # @param namespace [String]
72
91
  # @param singleton [Boolean]
73
92
  # @return [Rucoa::Definition::MethodDefinition, nil]
74
- # @example has the ability to find `IO.write` from `File.write`
93
+ # @example Supports inheritance
94
+ # source = Rucoa::Source.new(
95
+ # content: <<~RUBY,
96
+ # class A
97
+ # def foo
98
+ # end
99
+ # end
100
+ #
101
+ # class B < A
102
+ # end
103
+ # RUBY
104
+ # uri: 'file:///path/to/example.rb'
105
+ # )
75
106
  # definition_store = Rucoa::DefinitionStore.new
76
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
107
+ # definition_store.update_from(source)
77
108
  # subject = definition_store.find_method_definition_by(
78
- # method_name: 'write',
79
- # namespace: 'File',
80
- # singleton: true
109
+ # method_name: 'foo',
110
+ # namespace: 'B',
111
+ # singleton: false
81
112
  # )
82
- # expect(subject.fully_qualified_name).to eq('IO.write')
83
- def find_method_definition_by(method_name:, namespace:, singleton: false)
84
- definition = find_definition_by_fully_qualified_name(namespace)
113
+ # expect(subject.qualified_name).to eq('A#foo')
114
+ # @example supports `include`
115
+ # source = Rucoa::Source.new(
116
+ # content: <<~RUBY,
117
+ # module A
118
+ # def foo
119
+ # end
120
+ # end
121
+ #
122
+ # class B
123
+ # include A
124
+ # end
125
+ # RUBY
126
+ # uri: 'file:///path/to/example.rb'
127
+ # )
128
+ # definition_store = Rucoa::DefinitionStore.new
129
+ # definition_store.update_from(source)
130
+ # subject = definition_store.find_method_definition_by(
131
+ # method_name: 'foo',
132
+ # namespace: 'B',
133
+ # singleton: false
134
+ # )
135
+ # expect(subject.qualified_name).to eq('A#foo')
136
+ # @example supports `prepend`
137
+ # source = Rucoa::Source.new(
138
+ # content: <<~RUBY,
139
+ # module A
140
+ # def foo
141
+ # end
142
+ # end
143
+ #
144
+ # class B
145
+ # prepend A
146
+ #
147
+ # def foo
148
+ # end
149
+ # end
150
+ # RUBY
151
+ # uri: 'file:///path/to/example.rb'
152
+ # )
153
+ # definition_store = Rucoa::DefinitionStore.new
154
+ # definition_store.update_from(source)
155
+ # subject = definition_store.find_method_definition_by(
156
+ # method_name: 'foo',
157
+ # namespace: 'B',
158
+ # singleton: false
159
+ # )
160
+ # expect(subject.qualified_name).to eq('A#foo')
161
+ def find_method_definition_by(
162
+ method_name:,
163
+ namespace:,
164
+ singleton: false
165
+ )
166
+ definition = find_definition_by_qualified_name(namespace)
85
167
  return unless definition
86
168
 
87
- [
88
- namespace,
89
- *ancestor_definitions_of(definition).map(&:fully_qualified_name)
90
- ].find do |fully_qualified_name|
169
+ ancestors_of(definition).find do |ancestor|
91
170
  method_marker = singleton ? '.' : '#'
92
- fully_qualified_method_name = [
93
- fully_qualified_name,
171
+ qualified_method_name = [
172
+ ancestor.qualified_name,
94
173
  method_marker,
95
174
  method_name
96
175
  ].join
97
- definition = find_definition_by_fully_qualified_name(fully_qualified_method_name)
98
- break definition if definition
176
+ method_definition = find_definition_by_qualified_name(qualified_method_name)
177
+ break method_definition if method_definition
99
178
  end
100
179
  end
101
180
 
102
181
  # @param type [String]
103
182
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
104
- # @example includes ancestors' methods
183
+ # @example supports simple instance method
184
+ # source = Rucoa::Source.new(
185
+ # content: <<~RUBY,
186
+ # class A
187
+ # def foo
188
+ # end
189
+ # end
190
+ # RUBY
191
+ # uri: 'file:///path/to/example.rb'
192
+ # )
193
+ # definition_store = Rucoa::DefinitionStore.new
194
+ # definition_store.update_from(source)
195
+ # subject = definition_store.instance_method_definitions_of('A')
196
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
197
+ # @example supports inheritance
198
+ # source = Rucoa::Source.new(
199
+ # content: <<~RUBY,
200
+ # class A
201
+ # def foo
202
+ # end
203
+ # end
204
+ #
205
+ # class B < A
206
+ # end
207
+ # RUBY
208
+ # uri: 'file:///path/to/example.rb'
209
+ # )
210
+ # definition_store = Rucoa::DefinitionStore.new
211
+ # definition_store.update_from(source)
212
+ # subject = definition_store.instance_method_definitions_of('B')
213
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
214
+ # @example supports `include`
215
+ # source = Rucoa::Source.new(
216
+ # content: <<~RUBY,
217
+ # module A
218
+ # def foo
219
+ # end
220
+ # end
221
+ #
222
+ # class B
223
+ # include A
224
+ # end
225
+ # RUBY
226
+ # uri: 'file:///path/to/example.rb'
227
+ # )
105
228
  # definition_store = Rucoa::DefinitionStore.new
106
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
107
- # subject = definition_store.instance_method_definitions_of('File')
108
- # expect(subject.map(&:fully_qualified_name)).to include('IO#raw')
109
- # @example responds to `singleton<File>`
229
+ # definition_store.update_from(source)
230
+ # subject = definition_store.instance_method_definitions_of('B')
231
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
232
+ # @example supports `prepend`
233
+ # source = Rucoa::Source.new(
234
+ # content: <<~RUBY,
235
+ # module A
236
+ # def foo
237
+ # end
238
+ # end
239
+ #
240
+ # class B
241
+ # prepend A
242
+ #
243
+ # def foo
244
+ # end
245
+ # end
246
+ # RUBY
247
+ # uri: 'file:///path/to/example.rb'
248
+ # )
110
249
  # definition_store = Rucoa::DefinitionStore.new
111
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
112
- # subject = definition_store.instance_method_definitions_of('singleton<File>')
113
- # expect(subject.map(&:fully_qualified_name)).to include('IO.write')
250
+ # definition_store.update_from(source)
251
+ # subject = definition_store.instance_method_definitions_of('B')
252
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
114
253
  def instance_method_definitions_of(type)
115
254
  singleton_class_name = singleton_class_name_from(type)
116
255
  return singleton_method_definitions_of(singleton_class_name) if singleton_class_name
117
256
 
118
- class_or_module_definition = find_definition_by_fully_qualified_name(type)
257
+ class_or_module_definition = find_definition_by_qualified_name(type)
119
258
  return [] unless class_or_module_definition
120
259
 
121
- definitions = instance_method_definitions
122
- [
123
- class_or_module_definition,
124
- *ancestor_definitions_of(class_or_module_definition)
125
- ].map(&:fully_qualified_name).flat_map do |fully_qualified_type_name|
126
- definitions.select do |definition|
127
- definition.namespace == fully_qualified_type_name
260
+ method_definitions = self.method_definitions
261
+ ancestors_of(class_or_module_definition).flat_map do |ancestor|
262
+ method_definitions.select do |method_definition|
263
+ method_definition.namespace == ancestor.qualified_name &&
264
+ method_definition.instance_method?
128
265
  end
129
- end
266
+ end.uniq(&:method_name)
130
267
  end
131
268
 
132
269
  # @param type [String]
133
270
  # @return [Array<Rucoa::Definitions::MethodDefinition>]
134
- # @example returns singleton method definitions of File
271
+ # @example supports simple singleton method
272
+ # source = Rucoa::Source.new(
273
+ # content: <<~RUBY,
274
+ # class A
275
+ # def self.foo
276
+ # end
277
+ # end
278
+ # RUBY
279
+ # uri: 'file:///path/to/example.rb'
280
+ # )
281
+ # definition_store = Rucoa::DefinitionStore.new
282
+ # definition_store.update_from(source)
283
+ # subject = definition_store.singleton_method_definitions_of('A')
284
+ # expect(subject.map(&:qualified_name)).to include('A.foo')
285
+ # @example supports super class's singleton method
286
+ # source = Rucoa::Source.new(
287
+ # content: <<~RUBY,
288
+ # class A
289
+ # def self.foo
290
+ # end
291
+ # end
292
+ #
293
+ # class B < A
294
+ # end
295
+ # RUBY
296
+ # uri: 'file:///path/to/example.rb'
297
+ # )
135
298
  # definition_store = Rucoa::DefinitionStore.new
136
- # definition_store.bulk_add(Rucoa::DefinitionArchiver.load)
137
- # subject = definition_store.singleton_method_definitions_of('File')
138
- # expect(subject.map(&:fully_qualified_name)).to include('IO.write')
299
+ # definition_store.update_from(source)
300
+ # subject = definition_store.singleton_method_definitions_of('B')
301
+ # expect(subject.map(&:qualified_name)).to include('A.foo')
302
+ # @example supports extended module's instance method
303
+ # source = Rucoa::Source.new(
304
+ # content: <<~RUBY,
305
+ # module A
306
+ # def foo
307
+ # end
308
+ # end
309
+ #
310
+ # class B
311
+ # def self.foo
312
+ # end
313
+ # end
314
+ #
315
+ # class C < B
316
+ # extend A
317
+ # end
318
+ # RUBY
319
+ # uri: 'file:///path/to/example.rb'
320
+ # )
321
+ # definition_store = Rucoa::DefinitionStore.new
322
+ # definition_store.update_from(source)
323
+ # subject = definition_store.singleton_method_definitions_of('C')
324
+ # expect(subject.map(&:qualified_name)).to eq(%w[A#foo])
139
325
  def singleton_method_definitions_of(type)
140
- class_or_module_definition = find_definition_by_fully_qualified_name(type)
326
+ class_or_module_definition = find_definition_by_qualified_name(type)
141
327
  return [] unless class_or_module_definition
142
328
 
143
- definitions = singleton_method_definitions
144
- [
145
- class_or_module_definition,
146
- *ancestor_definitions_of(class_or_module_definition)
147
- ].map(&:fully_qualified_name).flat_map do |fully_qualified_type_name|
148
- definitions.select do |definition|
149
- definition.namespace == fully_qualified_type_name
329
+ method_definitions = self.method_definitions
330
+ ancestors_of(class_or_module_definition).flat_map do |ancestor|
331
+ method_definitions.select do |method_definition|
332
+ method_definition.namespace == ancestor.qualified_name &&
333
+ method_definition.singleton_method?
334
+ end + ancestor.extended_module_qualified_names.flat_map do |extended_module_qualified_name|
335
+ method_definitions.select do |method_definition|
336
+ method_definition.namespace == extended_module_qualified_name &&
337
+ method_definition.instance_method?
338
+ end
150
339
  end
151
- end
340
+ end.uniq(&:method_name)
152
341
  end
153
342
 
154
343
  # @param namespace [String]
@@ -159,42 +348,79 @@ module Rucoa
159
348
  end
160
349
  end
161
350
 
351
+ # @todo Search ancestors.
352
+ # @param unqualified_name [Rucoa::UnqualifiedName]
353
+ # @return [String]
354
+ def resolve_constant(unqualified_name)
355
+ (
356
+ unqualified_name.module_nesting.map do |prefix|
357
+ "#{prefix}::#{unqualified_name.chained_name}"
358
+ end + [unqualified_name.chained_name]
359
+ ).find do |candidate|
360
+ find_definition_by_qualified_name(candidate)
361
+ end || unqualified_name.chained_name
362
+ end
363
+
162
364
  private
163
365
 
164
366
  # @param source [Rucoa::Source]
165
367
  # @return [void]
166
- def delete_definitions_about(source)
167
- @fully_qualified_names_by_uri[source.uri].each do |fully_qualified_name|
168
- @definition_by_full_qualified_name.delete(fully_qualified_name)
368
+ def delete_definitions_in(source)
369
+ @qualified_names_by_uri[source.uri].each do |qualified_name|
370
+ @definition_by_qualified_name.delete(qualified_name)
169
371
  end
170
- @fully_qualified_names_by_uri.delete(source.uri)
372
+ @qualified_names_by_uri.delete(source.uri)
171
373
  end
172
374
 
173
375
  # @param type [String]
174
376
  # @return [String, nil]
175
377
  def singleton_class_name_from(type)
176
- type[/singleton<(\w+)>/, 1]
378
+ type[/singleton<([\w:]+)>/, 1]
379
+ end
380
+
381
+ # @param definition [Rucoa::Definitions::Class, Rucoa::Definitions::Module]
382
+ # @return [Array<Rucoa::Definitions::Class>] The classes and modules that are traced in method search (as `Module#ancestors` in Ruby)
383
+ def ancestors_of(definition)
384
+ if definition.is_a?(Rucoa::Definitions::ClassDefinition)
385
+ [definition, *super_class_definitions_of(definition)]
386
+ else
387
+ [definition]
388
+ end.flat_map do |base|
389
+ module_ancestors_of(base)
390
+ end
177
391
  end
178
392
 
179
- # @param class_or_module_definition [Rucoa::Definitions::Class, Rucoa::Definitions::Module]
180
- # @return [Array<Rucoa::Definitions::Class>]
181
- def ancestor_definitions_of(class_or_module_definition)
182
- return [] unless class_or_module_definition.is_a?(Definitions::ClassDefinition)
393
+ # @param definition [Rucoa::Definitions::Class, Rucoa::Definitions::Module]
394
+ # @return [Array<Rucoa::Definitions::Class, Rucoa::Definitions::Module>] An array of prepended, itself, and included definitions
395
+ def module_ancestors_of(definition)
396
+ [
397
+ *definition.prepended_module_qualified_names.filter_map do |qualified_name|
398
+ find_definition_by_qualified_name(qualified_name)
399
+ end.reverse,
400
+ definition,
401
+ *definition.included_module_qualified_names.filter_map do |qualified_name|
402
+ find_definition_by_qualified_name(qualified_name)
403
+ end.reverse
404
+ ]
405
+ end
183
406
 
407
+ # @param definition [Rucoa::Definitions::Class]
408
+ # @return [Array<Rucoa::Definitions::Class>] super "class"es (not including modules) in closest-first order
409
+ def super_class_definitions_of(definition)
184
410
  result = []
185
- class_definition = class_or_module_definition
186
- while (super_class_fully_qualified_name = class_definition.super_class_fully_qualified_name)
187
- class_definition = find_definition_by_fully_qualified_name(super_class_fully_qualified_name)
188
- break unless class_definition
411
+ while (super_class_qualified_name = definition.super_class_qualified_name)
412
+ super_definition = find_definition_by_qualified_name(super_class_qualified_name)
413
+ break unless super_definition
189
414
 
190
- result << class_definition
415
+ result << super_definition
416
+ definition = super_definition
191
417
  end
192
418
  result
193
419
  end
194
420
 
195
421
  # @return [Array<Rucoa::Definitions::Base>]
196
422
  def definitions
197
- @definition_by_full_qualified_name.values
423
+ @definition_by_qualified_name.values
198
424
  end
199
425
 
200
426
  # @return [Array<Rucoa::Definition::MethodDefinition>]
@@ -202,29 +428,48 @@ module Rucoa
202
428
  definitions.grep(Definitions::MethodDefinition)
203
429
  end
204
430
 
205
- # @return [Array<Rucoa::Definition::MethodDefinition>]
206
- def instance_method_definitions
207
- method_definitions.select(&:instance_method?)
208
- end
209
-
210
- # @return [Array<Rucoa::Definition::MethodDefinition>]
211
- def singleton_method_definitions
212
- method_definitions.select(&:singleton_method?)
213
- end
214
-
215
431
  # @return [Array<Rucoa::Definition::ConstantDefinition>]
216
432
  def constant_definitions
217
433
  definitions.grep(Definitions::ConstantDefinition)
218
434
  end
219
435
 
220
- # @param class_definition [Rucoa::Definitions::ClassDefinition]
221
- # @return [String]
222
- def resolve_super_class_of(class_definition)
223
- return 'Object' unless class_definition.super_class_chained_name
436
+ # @param source [Rucoa::Source]
437
+ # @return [void]
438
+ def add_definitions_in(source)
439
+ source.definitions.group_by do |definition|
440
+ definition.location.uri
441
+ end.each do |uri, definitions|
442
+ @qualified_names_by_uri[uri] += definitions.map(&:qualified_name)
443
+ definitions.each do |definition|
444
+ @definition_by_qualified_name[definition.qualified_name] = definition
445
+ end
446
+ end
447
+ end
448
+
449
+ # @param source [Rucoa::Source]
450
+ # @return [void]
451
+ def resolve_constants_in(source)
452
+ source.definitions.each do |definition|
453
+ next unless definition.is_a?(Definitions::ClassDefinition)
454
+
455
+ definition.super_class_qualified_name = resolve_constant(definition.super_class_unqualified_name)
456
+ definition.super_class_unqualified_name = nil
457
+
458
+ definition.extended_module_qualified_names = definition.extended_module_unqualified_names.map do |unqualified_name|
459
+ resolve_constant(unqualified_name)
460
+ end
461
+ definition.extended_module_unqualified_names = []
224
462
 
225
- class_definition.super_class_candidates.find do |candidate|
226
- find_definition_by_fully_qualified_name(candidate)
227
- end || class_definition.super_class_chained_name
463
+ definition.included_module_qualified_names = definition.included_module_unqualified_names.map do |unqualified_name|
464
+ resolve_constant(unqualified_name)
465
+ end
466
+ definition.included_module_unqualified_names = []
467
+
468
+ definition.prepended_module_qualified_names = definition.prepended_module_unqualified_names.map do |unqualified_name|
469
+ resolve_constant(unqualified_name)
470
+ end
471
+ definition.prepended_module_unqualified_names = []
472
+ end
228
473
  end
229
474
  end
230
475
  end
@@ -3,9 +3,20 @@
3
3
  module Rucoa
4
4
  module Definitions
5
5
  class Base
6
- # @return [String]
7
- def source_path
8
- raise ::NotImplementedError
6
+ # @return [String, nil]
7
+ attr_reader :description
8
+
9
+ # @return [Rucoa::Location, nil]
10
+ attr_accessor :location
11
+
12
+ # @param description [String, nil]
13
+ # @param location [Rucoa::Location, nil]
14
+ def initialize(
15
+ description:,
16
+ location:
17
+ )
18
+ @description = description
19
+ @location = location
9
20
  end
10
21
  end
11
22
  end