solargraph 0.16.0 → 0.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b84e595d8f1d4042f81bbfb7c27dc56913a0de8c
4
- data.tar.gz: 0ddfb700d7f1a8e10ec74f021954e1b9fa1375ce
3
+ metadata.gz: f0eee03f14aa3abbcaad7015b94ad8c33df50acc
4
+ data.tar.gz: b4485de9e9cf36aa8df184687cd9e5476e6b627d
5
5
  SHA512:
6
- metadata.gz: 171c19a787d30924b510b21d08c443c348d96d8c551fbefd03535dacc8e22fd289ace4f4f270e9469d61d813dc3c640e7ffed21d093826ef787173d0c80e5703
7
- data.tar.gz: 88637fc48b37ef6bc2c7e3e5f4051fc87fa8901c829bf94bffc877d884f7e47fca750182241cb3d2cf539811e7c9275358a0792a42e6ea666815b59bb94a24c8
6
+ metadata.gz: 3845660c58589e9d5222d9800fb3a48aa3970aa548468f643363d653b28b6b086c2d3b5c7d38afb322e6f45271292096acec3af7be24088d726957de7cbcbd1b
7
+ data.tar.gz: 3040f8716959d1378a2478dd23d76c8232f0451669860267f5cbb07b7be6ed2bda987039912d6501665e3cf7d3af6ff1d9e71e01792cdca949bfdf45c09f42b6
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ lib/**/*.rb
2
+ -e lib/yard-solargraph.rb
@@ -20,6 +20,10 @@ module Solargraph
20
20
  # @return [Array<Integer>]
21
21
  attr_reader :stubbed_lines
22
22
 
23
+ attr_reader :directives
24
+
25
+ attr_reader :path_macros
26
+
23
27
  include NodeMethods
24
28
 
25
29
  def initialize code, node, comments, filename, stubbed_lines = []
@@ -29,6 +33,7 @@ module Solargraph
29
33
  @node = root
30
34
  @comments = comments
31
35
  @directives = {}
36
+ @path_macros = {}
32
37
  @docstring_hash = associate_comments(node, comments)
33
38
  @filename = filename
34
39
  @mtime = (!filename.nil? and File.exist?(filename) ? File.mtime(filename) : nil)
@@ -54,6 +59,10 @@ module Solargraph
54
59
  gen_src = Source.virtual("def #{d.tag.name};end", filename)
55
60
  gen_pin = gen_src.method_pins.first
56
61
  method_pins.push Solargraph::Pin::Directed::Method.new(gen_src, gen_pin.node, ns, :instance, :public, docstring, gen_pin.name)
62
+ elsif d.tag.tag_name == 'macro'
63
+ # @todo Handle various types of macros (attach, new, whatever)
64
+ path = path_for(k.node)
65
+ @path_macros[path] = v
57
66
  else
58
67
  STDERR.puts "Nothing to do for directive: #{d}"
59
68
  end
@@ -61,6 +70,10 @@ module Solargraph
61
70
  end
62
71
  end
63
72
 
73
+ def macro path
74
+ @path_macros[path]
75
+ end
76
+
64
77
  def namespaces
65
78
  @namespace_nodes.keys
66
79
  end
@@ -160,6 +173,15 @@ module Solargraph
160
173
  parts.join('::')
161
174
  end
162
175
 
176
+ def path_for node
177
+ path = namespace_for(node) || ''
178
+ mp = (method_pins + attribute_pins).select{|p| p.node == node}.first
179
+ unless mp.nil?
180
+ path += (mp.scope == :instance ? '#' : '.') + mp.name
181
+ end
182
+ path
183
+ end
184
+
163
185
  def include? node
164
186
  @all_nodes.include? node
165
187
  end
@@ -394,11 +416,6 @@ module Solargraph
394
416
  stubs = []
395
417
  fixed_position = false
396
418
  tmp = code
397
- if !offset.nil? and offset > 0
398
- if tmp[offset - 1] == '.'
399
- tmp = tmp[0..offset-2] + '_' + tmp[offset..-1]
400
- end
401
- end
402
419
  begin
403
420
  node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
404
421
  Source.new(code, node, comments, filename, stubs)
@@ -407,11 +424,11 @@ module Solargraph
407
424
  tries += 1
408
425
  # Stub periods before the offset to retain the expected node tree
409
426
  if !offset.nil? and tmp[offset-1] == '.'
410
- tmp = tmp[0, offset-1] + '_' + tmp[offset..-1]
427
+ tmp = tmp[0, offset-1] + ';' + tmp[offset..-1]
411
428
  elsif !fixed_position and !offset.nil?
412
429
  fixed_position = true
413
430
  beg = beginning_of_line_from(tmp, offset)
414
- tmp = tmp[0, beg] + '#' + tmp[beg+1..-1]
431
+ tmp = "#{tmp[0, beg]}##{tmp[beg+1..-1]}"
415
432
  stubs.push(pos[0])
416
433
  elsif e.message.include?('token $end')
417
434
  tmp += "\nend"
@@ -23,6 +23,7 @@ module Solargraph
23
23
  @root_code_object ||= YARD::CodeObjects::RootObject.new(nil, 'root')
24
24
  end
25
25
 
26
+ # @param sources [Array<Solargraph::ApiMap::Source>] Sources for code objects
26
27
  def rake_yard sources
27
28
  code_object_map.clear
28
29
  sources.each do |s|
@@ -11,20 +11,9 @@ module Solargraph
11
11
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
12
12
  @@source_cache = {}
13
13
 
14
- KEYWORDS = [
15
- '__ENCODING__', '__LINE__', '__FILE__', 'BEGIN', 'END', 'alias', 'and',
16
- 'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', 'else',
17
- 'elsif', 'end', 'ensure', 'false', 'for', 'if', 'in', 'module', 'next',
18
- 'nil', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super',
19
- 'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield'
20
- ].freeze
21
-
22
- METHODS_RETURNING_SELF = [
23
- 'clone', 'dup', 'freeze', 'taint', 'untaint'
24
- ].freeze
25
-
26
14
  include NodeMethods
27
15
  include Solargraph::ApiMap::SourceToYard
16
+ include CoreFills
28
17
 
29
18
  # The root directory of the project. The ApiMap will search here for
30
19
  # additional files to parse and analyze.
@@ -58,6 +47,10 @@ module Solargraph
58
47
  yard_map
59
48
  end
60
49
 
50
+ # Get the configuration for the ApiMap's workspace. This method will
51
+ # initialize the settings from the workspace's root .solargraph.yml file
52
+ # if it exists.
53
+ #
61
54
  # @return [Solargraph::ApiMap::Config]
62
55
  def config reload = false
63
56
  @config = ApiMap::Config.new(@workspace) if @config.nil? or reload
@@ -78,6 +71,8 @@ module Solargraph
78
71
  @required ||= []
79
72
  end
80
73
 
74
+ # Get a YardMap associated with the current namespace.
75
+ #
81
76
  # @return [Solargraph::YardMap]
82
77
  def yard_map
83
78
  refresh
@@ -87,6 +82,8 @@ module Solargraph
87
82
  @yard_map
88
83
  end
89
84
 
85
+ # Get a LiveMap associated with the current namespace.
86
+ #
90
87
  # @return [Solargraph::LiveMap]
91
88
  def live_map
92
89
  @live_map ||= Solargraph::LiveMap.new(self)
@@ -130,19 +127,24 @@ module Solargraph
130
127
  virtualize code, filename
131
128
  end
132
129
 
130
+ # Refresh the ApiMap.
131
+ #
132
+ # @param force [Boolean] Perform a refresh even if the map is not "stale."
133
133
  def refresh force = false
134
134
  process_maps if @stale or force
135
135
  end
136
136
 
137
+ # True if a workspace file has been created, modified, or deleted since
138
+ # the last time the map was processed.
139
+ #
140
+ # @return [Boolean]
137
141
  def changed?
138
142
  current = config.calculated
139
143
  unless (Set.new(current) ^ workspace_files).empty?
140
- STDERR.puts "Change based on difference in file list"
141
144
  return true
142
145
  end
143
146
  current.each do |f|
144
147
  if !File.exist?(f) or File.mtime(f) != source_file_mtime(f)
145
- STDERR.puts "Change based on file #{f}"
146
148
  return true
147
149
  end
148
150
  end
@@ -159,39 +161,48 @@ module Solargraph
159
161
  @sources[filename].docstring_for(node)
160
162
  end
161
163
 
162
- # @deprecated Use get_docstring_for instead.
163
- def get_comment_for node
164
- get_docstring_for node
165
- end
166
-
164
+ # An array of suggestions based on Ruby keywords (`if`, `end`, etc.).
165
+ #
167
166
  # @return [Array<Solargraph::Suggestion>]
168
- def self.get_keywords
167
+ def self.keywords
169
168
  @keyword_suggestions ||= KEYWORDS.map{ |s|
170
169
  Suggestion.new(s.to_s, kind: Suggestion::KEYWORD, detail: 'Keyword')
171
170
  }.freeze
172
171
  end
173
172
 
173
+ # An array of namespace names defined in the ApiMap.
174
+ #
174
175
  # @return [Array<String>]
175
176
  def namespaces
176
177
  refresh
177
178
  namespace_map.keys
178
179
  end
179
180
 
181
+ # True if the namespace exists.
182
+ #
183
+ # @param name [String] The namespace to match
184
+ # @param root [String] The context to search
185
+ # @return [Boolean]
180
186
  def namespace_exists? name, root = ''
181
187
  !find_fully_qualified_namespace(name, root).nil?
182
188
  end
183
189
 
184
- # @deprecated Use get_constants instead.
185
- def namespaces_in name, root = ''
186
- get_constants name, root
187
- end
188
-
190
+ # Get an array of constant pins defined in the ApiMap. (This method does
191
+ # not include constants from external gems or the Ruby core.)
192
+ #
193
+ # @param namespace [String] The namespace to match
194
+ # @param root [String] The context to search
189
195
  # @return [Array<Solargraph::Pin::Constant>]
190
196
  def get_constant_pins namespace, root
191
197
  fqns = find_fully_qualified_namespace(namespace, root)
192
198
  @const_pins[fqns] || []
193
199
  end
194
200
 
201
+ # Get suggestions for constants in the specified namespace. The result
202
+ # will include constant variables, classes, and modules.
203
+ #
204
+ # @param namespace [String] The namespace to match
205
+ # @param root [String] The context to search
195
206
  # @return [Array<Solargraph::Suggestion>]
196
207
  def get_constants namespace, root = ''
197
208
  result = []
@@ -212,17 +223,11 @@ module Solargraph
212
223
  result
213
224
  end
214
225
 
215
- def find_namespace_pins fqns
216
- set = nil
217
- if fqns.include?('::')
218
- set = @namespace_pins[fqns.split('::')[0..-2].join('::')]
219
- else
220
- set = @namespace_pins['']
221
- end
222
- return [] if set.nil?
223
- set.select{|p| p.path == fqns}
224
- end
225
-
226
+ # Get a fully qualified namespace name. This method will start the search
227
+ # in the specified root until it finds a match for the name.
228
+ #
229
+ # @param name [String] The namespace to match
230
+ # @param root [String] The context to search
226
231
  # @return [String]
227
232
  def find_fully_qualified_namespace name, root = '', skip = []
228
233
  refresh
@@ -263,18 +268,22 @@ module Solargraph
263
268
  result
264
269
  end
265
270
 
266
- def get_namespace_nodes(fqns)
267
- return file_nodes if fqns == '' or fqns.nil?
268
- refresh
269
- namespace_map[fqns] || []
270
- end
271
-
271
+ # Get an array of instance variable pins defined in specified namespace
272
+ # and scope.
273
+ #
274
+ # @param namespace [String] A fully qualified namespace
275
+ # @param scope [Symbol] :instance or :class
272
276
  # @return [Array<Solargraph::Pin::InstanceVariable>]
273
277
  def get_instance_variable_pins(namespace, scope = :instance)
274
278
  refresh
275
279
  (@ivar_pins[namespace] || []).select{ |pin| pin.scope == scope }
276
280
  end
277
281
 
282
+ # Get an array of instance variable suggestions defined in specified
283
+ # namespace and scope.
284
+ #
285
+ # @param namespace [String] A fully qualified namespace
286
+ # @param scope [Symbol] :instance or :class
278
287
  # @return [Array<Solargraph::Suggestion>]
279
288
  def get_instance_variables(namespace, scope = :instance)
280
289
  refresh
@@ -332,7 +341,13 @@ module Solargraph
332
341
  return nil if pins.nil?
333
342
  pin = pins.select{|p| p.name == var and p.scope == scope}.first
334
343
  return nil if pin.nil?
335
- pin.return_type
344
+ type = pin.return_type
345
+ if type.nil?
346
+ zparts = resolve_node_signature(pin.assignment_node).split('.')
347
+ ztype = infer_signature_type(zparts[0..-2].join('.'), namespace, scope: :instance, call_node: pin.assignment_node)
348
+ type = get_return_type_from_macro(ztype, zparts[-1], pin.assignment_node, :instance, [:public, :private, :protected])
349
+ end
350
+ type
336
351
  end
337
352
 
338
353
  # @return [String]
@@ -366,6 +381,8 @@ module Solargraph
366
381
 
367
382
  # @return [String]
368
383
  def infer_assignment_node_type node, namespace
384
+ cached = cache.get_assignment_node_type(node, namespace)
385
+ return cached unless cached.nil?
369
386
  name_i = (node.type == :casgn ? 1 : 0)
370
387
  sig_i = (node.type == :casgn ? 2 : 1)
371
388
  type = infer_literal_node_type(node.children[sig_i])
@@ -373,13 +390,32 @@ module Solargraph
373
390
  sig = resolve_node_signature(node.children[sig_i])
374
391
  # Avoid infinite loops from variable assignments that reference themselves
375
392
  return nil if node.children[name_i].to_s == sig.split('.').first
376
- type = infer_signature_type(sig, namespace)
393
+ type = infer_signature_type(sig, namespace, call_node: node.children[sig_i])
377
394
  end
395
+ cache.set_assignment_node_type(node, namespace, type)
378
396
  type
379
397
  end
380
398
 
399
+ def get_call_arguments node
400
+ return [] unless node.type == :send
401
+ result = []
402
+ node.children[2..-1].each do |c|
403
+ result.push unpack_name(c)
404
+ end
405
+ result
406
+ end
407
+
408
+ # Get the return type for a signature within the specified namespace and
409
+ # scope.
410
+ #
411
+ # @example
412
+ # api_map.infer_signature_type('String.new', '') #=> 'String'
413
+ #
414
+ # @param signature [String]
415
+ # @param namespace [String] A fully qualified namespace
416
+ # @param scope [Symbol] :class or :instance
381
417
  # @return [String]
382
- def infer_signature_type signature, namespace, scope: :class
418
+ def infer_signature_type signature, namespace, scope: :class, call_node: nil
383
419
  namespace ||= ''
384
420
  if cache.has_signature_type?(signature, namespace, scope)
385
421
  return cache.get_signature_type(signature, namespace, scope)
@@ -396,20 +432,20 @@ module Solargraph
396
432
  end
397
433
  result = nil
398
434
  if namespace.end_with?('#class')
399
- result = infer_signature_type signature, namespace[0..-7], scope: (scope == :class ? :instance : :class)
435
+ result = infer_signature_type signature, namespace[0..-7], scope: (scope == :class ? :instance : :class), call_node: call_node
400
436
  else
401
437
  parts = signature.split('.', 2)
402
438
  if parts[0].start_with?('@@')
403
439
  type = infer_class_variable(parts[0], namespace)
404
440
  if type.nil? or parts.empty?
405
- result = inner_infer_signature_type(parts[1], type, scope: :instance)
441
+ result = inner_infer_signature_type(parts[1], type, scope: :instance, call_node: call_node)
406
442
  else
407
443
  result = type
408
444
  end
409
445
  elsif parts[0].start_with?('@')
410
446
  type = infer_instance_variable(parts[0], namespace, scope)
411
447
  if type.nil? or parts.empty?
412
- result = inner_infer_signature_type(parts[1], type, scope: :instance)
448
+ result = inner_infer_signature_type(parts[1], type, scope: :instance, call_node: call_node)
413
449
  else
414
450
  result = type
415
451
  end
@@ -417,21 +453,29 @@ module Solargraph
417
453
  type = find_fully_qualified_namespace(parts[0], namespace)
418
454
  if type.nil?
419
455
  # It's a method call
420
- type = inner_infer_signature_type(parts[0], namespace, scope: scope)
421
- if parts[1].nil?
456
+ type = inner_infer_signature_type(parts[0], namespace, scope: scope, call_node: call_node)
457
+ if parts.length < 2
458
+ if type.nil? and !parts.length.nil?
459
+ path = "#{clean_namespace_string(namespace)}#{scope == :class ? '.' : '#'}#{parts[0]}"
460
+ subtypes = get_subtypes(namespace)
461
+ type = subtypes[0] if METHODS_RETURNING_SUBTYPES.include?(path)
462
+ end
422
463
  result = type
423
464
  else
424
- result = inner_infer_signature_type(parts[1], type, scope: :instance)
465
+ result = inner_infer_signature_type(parts[1], type, scope: :instance, call_node: call_node)
425
466
  end
426
467
  else
427
- result = inner_infer_signature_type(parts[1], type, scope: :class)
468
+ result = inner_infer_signature_type(parts[1], type, scope: :class, call_node: call_node)
428
469
  end
470
+ result = type if result == 'self'
429
471
  end
430
472
  end
431
473
  cache.set_signature_type signature, namespace, scope, result
432
474
  result
433
475
  end
434
476
 
477
+ # Get the namespace's type (Class or Module).
478
+ #
435
479
  # @param [String] A fully qualified namespace
436
480
  # @return [Symbol] :class, :module, or nil
437
481
  def get_namespace_type fqns
@@ -499,9 +543,7 @@ module Solargraph
499
543
  def get_instance_methods(namespace, root = '', visibility: [:public])
500
544
  refresh
501
545
  namespace = clean_namespace_string(namespace)
502
- if namespace.end_with?('#class')
503
- return get_methods(namespace.split('#').first, root, visibility: visibility)
504
- elsif namespace.end_with?('#module')
546
+ if namespace.end_with?('#class') or namespace.end_with?('#module')
505
547
  return get_methods(namespace.split('#').first, root, visibility: visibility)
506
548
  end
507
549
  meths = []
@@ -531,21 +573,9 @@ module Solargraph
531
573
  meths
532
574
  end
533
575
 
534
- # @return [Array<String>]
535
- def get_include_strings_from *nodes
536
- arr = []
537
- nodes.each { |node|
538
- next unless node.kind_of?(AST::Node)
539
- arr.push unpack_name(node.children[2]) if (node.type == :send and node.children[1] == :include)
540
- node.children.each { |n|
541
- arr += get_include_strings_from(n) if n.kind_of?(AST::Node) and n.type != :class and n.type != :module and n.type != :sclass
542
- }
543
- }
544
- arr
545
- end
546
-
547
576
  # Update the ApiMap with the most recent version of the specified file.
548
577
  #
578
+ # @param filename [String]
549
579
  def update filename
550
580
  filename.gsub!(/\\/, '/')
551
581
  if filename.end_with?('.rb')
@@ -570,11 +600,16 @@ module Solargraph
570
600
  end
571
601
  end
572
602
 
603
+ # All sources generated from workspace files.
604
+ #
573
605
  # @return [Array<Solargraph::ApiMap::Source>]
574
606
  def sources
575
607
  @sources.values
576
608
  end
577
609
 
610
+ # Get an array of all suggestions that match the specified path.
611
+ #
612
+ # @param path [String] The path to find
578
613
  # @return [Array<Solargraph::Suggestion>]
579
614
  def get_path_suggestions path
580
615
  refresh
@@ -599,6 +634,12 @@ module Solargraph
599
634
  result
600
635
  end
601
636
 
637
+ # Get a list of documented paths that match the query.
638
+ #
639
+ # @example
640
+ # api_map.query('str') # Results will include `String` and `Struct`
641
+ #
642
+ # @param query [String] The text to match
602
643
  # @return [Array<String>]
603
644
  def search query
604
645
  refresh
@@ -613,6 +654,12 @@ module Solargraph
613
654
  found.concat(yard_map.search(query)).uniq.sort
614
655
  end
615
656
 
657
+ # Get YARD documentation for the specified path.
658
+ #
659
+ # @example
660
+ # api_map.document('String#split')
661
+ #
662
+ # @param path [String] The path to find
616
663
  # @return [Array<YARD::CodeObject::Base>]
617
664
  def document path
618
665
  refresh
@@ -634,6 +681,7 @@ module Solargraph
634
681
  def clear
635
682
  @stale = false
636
683
  namespace_map.clear
684
+ path_macros.clear
637
685
  @required = config.required.clone
638
686
  end
639
687
 
@@ -756,6 +804,7 @@ module Solargraph
756
804
  @namespace_pins[pin.namespace] ||= []
757
805
  @namespace_pins[pin.namespace].push pin
758
806
  end
807
+ path_macros.merge! source.path_macros
759
808
  source.required.each do |r|
760
809
  required.push r
761
810
  end
@@ -835,7 +884,7 @@ module Solargraph
835
884
  #
836
885
  # @return [String] The fully qualified namespace for the signature's type
837
886
  # or nil if a type could not be determined
838
- def inner_infer_signature_type signature, namespace, scope: :instance, top: true
887
+ def inner_infer_signature_type signature, namespace, scope: :instance, top: true, call_node: nil
839
888
  return nil if signature.nil?
840
889
  signature.gsub!(/\.$/, '')
841
890
  if signature.empty?
@@ -863,7 +912,8 @@ module Solargraph
863
912
  end
864
913
  if scope == :class and part == 'new'
865
914
  scope = :instance
866
- elsif !METHODS_RETURNING_SELF.include?(part)
915
+ else
916
+ curtype = type
867
917
  type = nil
868
918
  visibility = [:public]
869
919
  visibility.concat [:private, :protected] if top
@@ -873,9 +923,22 @@ module Solargraph
873
923
  tmp = get_methods(namespace, visibility: visibility)
874
924
  end
875
925
  tmp.concat get_instance_methods('Kernel', visibility: [:public]) if top
876
- meth = tmp.select{|s| s.label == part}.first
877
- return nil if meth.nil? or meth.return_type.nil?
878
- type = meth.return_type
926
+ matches = tmp.select{|s| s.label == part}
927
+ return nil if matches.empty?
928
+ matches.each do |m|
929
+ type = get_return_type_from_macro(namespace, signature, call_node, scope, visibility)
930
+ if type.nil?
931
+ if METHODS_RETURNING_SELF.include?(m.path)
932
+ type = curtype
933
+ elsif METHODS_RETURNING_SUBTYPES.include?(m.path)
934
+ subtypes = get_subtypes(namespace)
935
+ type = subtypes[0]
936
+ else
937
+ type = m.return_type
938
+ end
939
+ end
940
+ break unless type.nil?
941
+ end
879
942
  scope = :instance
880
943
  end
881
944
  top = false
@@ -985,5 +1048,73 @@ module Solargraph
985
1048
  end
986
1049
  nil
987
1050
  end
1051
+
1052
+ # @return [Array<Solargraph::Pin::Namespace>]
1053
+ def find_namespace_pins fqns
1054
+ set = nil
1055
+ if fqns.include?('::')
1056
+ set = @namespace_pins[fqns.split('::')[0..-2].join('::')]
1057
+ else
1058
+ set = @namespace_pins['']
1059
+ end
1060
+ return [] if set.nil?
1061
+ set.select{|p| p.path == fqns}
1062
+ end
1063
+
1064
+ def get_namespace_nodes(fqns)
1065
+ return file_nodes if fqns == '' or fqns.nil?
1066
+ refresh
1067
+ namespace_map[fqns] || []
1068
+ end
1069
+
1070
+ # @return [Array<String>]
1071
+ def get_include_strings_from *nodes
1072
+ arr = []
1073
+ nodes.each { |node|
1074
+ next unless node.kind_of?(AST::Node)
1075
+ arr.push unpack_name(node.children[2]) if (node.type == :send and node.children[1] == :include)
1076
+ node.children.each { |n|
1077
+ arr += get_include_strings_from(n) if n.kind_of?(AST::Node) and n.type != :class and n.type != :module and n.type != :sclass
1078
+ }
1079
+ }
1080
+ arr
1081
+ end
1082
+
1083
+ # @todo DRY this method. It's duplicated in CodeMap
1084
+ def get_subtypes type
1085
+ return nil if type.nil?
1086
+ match = type.match(/<([a-z0-9_:, ]*)>/i)
1087
+ return [] if match.nil?
1088
+ match[1].split(',').map(&:strip)
1089
+ end
1090
+
1091
+ # @return [Hash]
1092
+ def path_macros
1093
+ @path_macros ||= {}
1094
+ end
1095
+
1096
+ def get_return_type_from_macro namespace, signature, call_node, scope, visibility
1097
+ return nil if signature.empty? or signature.include?('.') or call_node.nil?
1098
+ path = "#{namespace}#{scope == :class ? '.' : '#'}#{signature}"
1099
+ macmeth = get_path_suggestions(path).first
1100
+ type = nil
1101
+ unless macmeth.nil?
1102
+ macro = path_macros[macmeth.path]
1103
+ macro = macro.first unless macro.nil?
1104
+ if macro.nil? and !macmeth.code_object.nil? and !macmeth.code_object.base_docstring.nil? and macmeth.code_object.base_docstring.all.include?('@!macro')
1105
+ all = YARD::Docstring.parser.parse(macmeth.code_object.base_docstring.all).directives
1106
+ macro = all.select{|m| m.tag.tag_name == 'macro'}.first
1107
+ end
1108
+ unless macro.nil?
1109
+ docstring = YARD::Docstring.parser.parse(macro.tag.text).to_docstring
1110
+ rt = docstring.tag(:return)
1111
+ unless rt.nil? or rt.types.nil? or call_node.nil?
1112
+ args = get_call_arguments(call_node)
1113
+ type = "#{args[rt.types[0][1..-1].to_i-1]}"
1114
+ end
1115
+ end
1116
+ end
1117
+ type
1118
+ end
988
1119
  end
989
1120
  end