rucoa 0.9.0 → 0.11.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.
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