jsduck 5.0.0.beta01 → 5.0.0.beta2

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 (95) hide show
  1. data/.travis.yml +1 -0
  2. data/README.md +6 -32
  3. data/Rakefile +5 -5
  4. data/bin/jsduck +0 -1
  5. data/js-classes/String.js +3 -5
  6. data/jsduck.gemspec +3 -2
  7. data/lib/jsduck/aggregator.rb +1 -3
  8. data/lib/jsduck/app.rb +2 -2
  9. data/lib/jsduck/categories/file.rb +0 -6
  10. data/lib/jsduck/class.rb +1 -2
  11. data/lib/jsduck/doc/parser.rb +12 -5
  12. data/lib/jsduck/doc/scanner.rb +6 -0
  13. data/lib/jsduck/doc/standard_tag_parser.rb +10 -5
  14. data/lib/jsduck/doc/subproperties.rb +9 -2
  15. data/lib/jsduck/docs_code_comparer.rb +20 -7
  16. data/lib/jsduck/exporter/app.rb +18 -13
  17. data/lib/jsduck/exporter/full.rb +18 -21
  18. data/lib/jsduck/format/doc.rb +0 -1
  19. data/lib/jsduck/format/html_stack.rb +1 -2
  20. data/lib/jsduck/format/subproperties.rb +2 -2
  21. data/lib/jsduck/inline/auto_link.rb +1 -1
  22. data/lib/jsduck/inline/img.rb +1 -1
  23. data/lib/jsduck/inline/link.rb +4 -6
  24. data/lib/jsduck/inline/video.rb +1 -2
  25. data/lib/jsduck/js/ast.rb +1 -1
  26. data/lib/jsduck/js/esprima.rb +24 -9
  27. data/lib/jsduck/logger.rb +50 -12
  28. data/lib/jsduck/members_index.rb +1 -2
  29. data/lib/jsduck/merger.rb +20 -2
  30. data/lib/jsduck/options.rb +125 -24
  31. data/lib/jsduck/process/accessors.rb +21 -8
  32. data/lib/jsduck/process/enums.rb +2 -3
  33. data/lib/jsduck/process/ext4_events.rb +2 -1
  34. data/lib/jsduck/process/global_members.rb +1 -2
  35. data/lib/jsduck/process/importer.rb +2 -6
  36. data/lib/jsduck/process/inherit_class.rb +58 -0
  37. data/lib/jsduck/process/inherit_doc.rb +6 -175
  38. data/lib/jsduck/process/inherit_members.rb +257 -0
  39. data/lib/jsduck/process/lint.rb +18 -7
  40. data/lib/jsduck/process/overrides.rb +1 -2
  41. data/lib/jsduck/render/class.rb +1 -1
  42. data/lib/jsduck/tag/alias.rb +2 -1
  43. data/lib/jsduck/tag/alternate_class_names.rb +1 -0
  44. data/lib/jsduck/tag/aside.rb +3 -3
  45. data/lib/jsduck/tag/author.rb +18 -3
  46. data/lib/jsduck/tag/autodetected.rb +21 -0
  47. data/lib/jsduck/tag/boolean_tag.rb +1 -1
  48. data/lib/jsduck/tag/cfg.rb +7 -3
  49. data/lib/jsduck/tag/class.rb +1 -1
  50. data/lib/jsduck/tag/class_list_tag.rb +1 -1
  51. data/lib/jsduck/tag/constructor.rb +1 -1
  52. data/lib/jsduck/tag/css_var.rb +1 -1
  53. data/lib/jsduck/tag/default.rb +1 -1
  54. data/lib/jsduck/tag/deprecated_tag.rb +1 -1
  55. data/lib/jsduck/tag/docauthor.rb +2 -0
  56. data/lib/jsduck/tag/enum.rb +2 -2
  57. data/lib/jsduck/tag/event.rb +1 -1
  58. data/lib/jsduck/tag/extends.rb +1 -1
  59. data/lib/jsduck/tag/ftype.rb +2 -1
  60. data/lib/jsduck/tag/inheritdoc.rb +1 -1
  61. data/lib/jsduck/tag/localdoc.rb +33 -0
  62. data/lib/jsduck/tag/markdown.rb +1 -1
  63. data/lib/jsduck/tag/member.rb +1 -1
  64. data/lib/jsduck/tag/method.rb +1 -1
  65. data/lib/jsduck/tag/mixins.rb +1 -0
  66. data/lib/jsduck/tag/override.rb +1 -1
  67. data/lib/jsduck/tag/param.rb +16 -5
  68. data/lib/jsduck/tag/preventable.rb +1 -1
  69. data/lib/jsduck/tag/property.rb +7 -3
  70. data/lib/jsduck/tag/ptype.rb +2 -1
  71. data/lib/jsduck/tag/requires.rb +1 -0
  72. data/lib/jsduck/tag/return.rb +2 -1
  73. data/lib/jsduck/tag/since.rb +1 -5
  74. data/lib/jsduck/tag/tag.rb +21 -12
  75. data/lib/jsduck/tag/throws.rb +2 -1
  76. data/lib/jsduck/tag/type.rb +2 -2
  77. data/lib/jsduck/tag/uses.rb +1 -0
  78. data/lib/jsduck/tag/xtype.rb +2 -1
  79. data/lib/jsduck/tag_loader.rb +26 -15
  80. data/lib/jsduck/tag_registry.rb +20 -11
  81. data/lib/jsduck/web/css.rb +22 -0
  82. data/lib/jsduck/web/data.rb +50 -0
  83. data/lib/jsduck/web/icons.rb +31 -0
  84. data/lib/jsduck/web/index_html.rb +88 -0
  85. data/lib/jsduck/web/search.rb +148 -0
  86. data/lib/jsduck/{source/writer.rb → web/source.rb} +2 -2
  87. data/lib/jsduck/web/template.rb +52 -0
  88. data/lib/jsduck/web/writer.rb +84 -0
  89. metadata +513 -488
  90. data/lib/jsduck/app_data.rb +0 -41
  91. data/lib/jsduck/icons.rb +0 -29
  92. data/lib/jsduck/index_html.rb +0 -84
  93. data/lib/jsduck/search_data.rb +0 -146
  94. data/lib/jsduck/template_dir.rb +0 -50
  95. data/lib/jsduck/web_writer.rb +0 -87
@@ -3,6 +3,17 @@ require 'jsduck/logger'
3
3
  module JsDuck
4
4
  module Process
5
5
 
6
+ # Expands accessors.
7
+ #
8
+ # Looks up configs with @accessor tag (or configs defined inside
9
+ # config: {} or eventedConfig: {} block).
10
+ #
11
+ # For such config "foo" it generates:
12
+ #
13
+ # - getter "getFoo"
14
+ # - setter "setFoo"
15
+ # - event "foochange" (when tagged with @evented)
16
+ #
6
17
  class Accessors
7
18
  def initialize(classes)
8
19
  @classes = classes
@@ -13,6 +24,8 @@ module JsDuck
13
24
  @classes.each_value {|cls| process(cls) }
14
25
  end
15
26
 
27
+ private
28
+
16
29
  # Given a class, generates accessor methods to configs with
17
30
  # @accessor tag. Modifies the class by adding these methods.
18
31
  # When class already contains a getter or setter, the method is
@@ -113,15 +126,15 @@ module JsDuck
113
126
  }, cfg)
114
127
  end
115
128
 
129
+ # Copy over from @cfg all the fields that aren't already present.
130
+ # Except :type and :default which don't make sense for methods and events.
116
131
  def add_shared(hash, cfg)
117
- hash.merge!({
118
- :owner => cfg[:owner],
119
- :files => cfg[:files],
120
- :private => cfg[:private],
121
- :protected => cfg[:protected],
122
- :autodetected => cfg[:autodetected],
123
- :hide => cfg[:hide],
124
- })
132
+ ignored_fields = [:type, :default, :accessor, :evented]
133
+
134
+ cfg.each_pair do |key, value|
135
+ hash[key] = value unless ignored_fields.include?(key) || hash[key]
136
+ end
137
+ hash
125
138
  end
126
139
 
127
140
  def upcase_first(str)
@@ -60,8 +60,7 @@ module JsDuck
60
60
  if m[:tagname] == :property
61
61
  false
62
62
  else
63
- f = m[:files][0]
64
- Logger.warn(:enum, "Enums can only contain properties, #{m[:tagname]} found instead.", f[:filename], f[:linenr])
63
+ Logger.warn(:enum, "Enums can only contain properties, #{m[:tagname]} found instead.", m[:files][0])
65
64
  true
66
65
  end
67
66
  end
@@ -71,7 +70,7 @@ module JsDuck
71
70
  # values default to being public.
72
71
  def strip_inheritdoc(cls)
73
72
  cls[:members].each do |p|
74
- p[:inheritdoc] = nil if p[:autodetected]
73
+ p[:inheritdoc] = nil if p[:autodetected] && p[:autodetected][:tagname]
75
74
  end
76
75
  end
77
76
 
@@ -34,7 +34,8 @@ module JsDuck
34
34
  :tagname => :params,
35
35
  :name => "eOpts",
36
36
  :type => "Object",
37
- :doc => "The options object passed to {@link Ext.util.Observable#addListener}."
37
+ :doc => "The options object passed to {@link Ext.util.Observable#addListener}.",
38
+ :ext4_auto_param => true,
38
39
  }
39
40
 
40
41
  end
@@ -21,8 +21,7 @@ module JsDuck
21
21
  @classes_hash["global"][:members].each do |m|
22
22
  type = m[:tagname].to_s
23
23
  name = m[:name]
24
- file = m[:files][0]
25
- Logger.warn(:global, "Global #{type}: #{name}", file[:filename], file[:linenr])
24
+ Logger.warn(:global, "Global #{type}: #{name}", m[:files][0])
26
25
  end
27
26
 
28
27
  # Throw away the "global" class when --ignore-global option used
@@ -42,12 +42,8 @@ module JsDuck
42
42
  # creates index of all class members
43
43
  def members_id_index(json)
44
44
  index = {}
45
- ["members", "statics"].each do |group_name|
46
- json[group_name].each_pair do |tagname, members|
47
- members.each do |m|
48
- index[m["id"]] = true
49
- end
50
- end
45
+ json["members"].each do |m|
46
+ index[m["id"]] = true
51
47
  end
52
48
  index
53
49
  end
@@ -0,0 +1,58 @@
1
+ require 'jsduck/logger'
2
+
3
+ module JsDuck
4
+ module Process
5
+
6
+ # Deals with inheriting class documentation.
7
+ class InheritClass
8
+ def initialize(relations)
9
+ @relations = relations
10
+ end
11
+
12
+ # Inherits docs for class.
13
+ #
14
+ # For class we only inherit the value of :doc field.
15
+ #
16
+ # When the class we're inheriting from also has @inheritdoc tag,
17
+ # we first recursively resolve the inheritance of that class and
18
+ # only afterwards inherit to the current class.
19
+ def resolve(cls)
20
+ return unless cls[:inheritdoc]
21
+
22
+ parent = find_parent(cls)
23
+ if parent && parent[:inheritdoc]
24
+ resolve(parent)
25
+ end
26
+
27
+ if parent
28
+ cls[:doc] = parent[:doc] if cls[:doc].empty?
29
+ end
30
+
31
+ cls[:inheritdoc] = nil
32
+ end
33
+
34
+ private
35
+
36
+ def find_parent(cls)
37
+ if cls[:inheritdoc][:cls]
38
+ # @inheritdoc MyClass
39
+ parent = @relations[cls[:inheritdoc][:cls]]
40
+ return warn("class not found", cls) unless parent
41
+ else
42
+ # @inheritdoc
43
+ parent = cls.parent
44
+ return warn("parent class not found", cls) unless parent
45
+ end
46
+
47
+ return parent
48
+ end
49
+
50
+ def warn(msg, cls)
51
+ Logger.warn(:inheritdoc, "@inheritdoc #{cls[:inheritdoc][:cls]} - #{msg}", cls[:files][0])
52
+ return nil
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -1,5 +1,7 @@
1
1
  require 'jsduck/logger'
2
2
  require 'jsduck/class'
3
+ require 'jsduck/process/inherit_class'
4
+ require 'jsduck/process/inherit_members'
3
5
 
4
6
  module JsDuck
5
7
  module Process
@@ -8,189 +10,18 @@ module JsDuck
8
10
  class InheritDoc
9
11
  def initialize(relations)
10
12
  @relations = relations
13
+ @inherit_class = InheritClass.new(@relations)
14
+ @inherit_members = InheritMembers.new(@relations)
11
15
  end
12
16
 
13
17
  # Performs all inheriting
14
18
  def process_all!
15
19
  @relations.each do |cls|
16
- resolve_class(cls) if cls[:inheritdoc]
17
-
18
- new_cfgs = []
19
- cls.all_local_members.each do |member|
20
- if member[:inheritdoc]
21
- resolve(member, new_cfgs)
22
- end
23
- end
24
- move_cfgs(cls, new_cfgs) if new_cfgs.length > 0
25
- end
26
- end
27
-
28
- private
29
-
30
- # Copy over doc/params/return from parent member.
31
- def resolve(m, new_cfgs)
32
- parent = find_parent(m)
33
-
34
- if m[:inheritdoc] && parent
35
- m[:doc] = (m[:doc] + "\n\n" + parent[:doc]).strip
36
- m[:params] = parent[:params] if parent[:params]
37
- m[:return] = parent[:return] if parent[:return]
38
- m[:type] = parent[:type] if parent[:type]
39
-
40
- if m[:autodetected]
41
- m[:deprecated] = parent[:deprecated] if parent[:deprecated] && !m[:deprecated]
42
- end
43
-
44
- # remember properties that have changed to configs
45
- if m[:autodetected] && m[:tagname] != parent[:tagname]
46
- new_cfgs << m
47
- end
48
- end
49
-
50
- resolve_visibility(m, parent)
51
- end
52
-
53
- # Changes given properties into configs within class
54
- def move_cfgs(cls, members)
55
- members.each do |m|
56
- m[:tagname] = :cfg
57
- end
58
- # Ask class to update its internal caches for these members
59
- cls.update_members!(members)
60
- end
61
-
62
- # For auto-detected members/classes (which have @private == :inherit)
63
- # Use the visibility from parent class (defaulting to private when no parent).
64
- def resolve_visibility(m, parent)
65
- if m[:autodetected] && !JsDuck::Class.constructor?(m)
66
- if !parent || parent[:private]
67
- m[:private] = true
68
- end
69
-
70
- m[:protected] = true if parent && parent[:protected]
71
- end
72
- end
73
-
74
- # Finds parent member of the given member. When @inheritdoc names
75
- # a member to inherit from, finds that member instead.
76
- #
77
- # If the parent also has @inheritdoc, continues recursively.
78
- def find_parent(m)
79
-
80
- inherit = m[:inheritdoc] || {}
81
- if inherit[:cls]
82
- # @inheritdoc MyClass#member
83
- parent_cls = @relations[m[:inheritdoc][:cls]]
84
- return warn("class not found", m) unless parent_cls
85
-
86
- parent = lookup_member(parent_cls, m)
87
- return warn("member not found", m) unless parent
88
-
89
- elsif inherit[:member]
90
- # @inheritdoc #member
91
- parent = lookup_member(@relations[m[:owner]], m)
92
- return warn("member not found", m) unless parent
93
-
94
- else
95
- # @inheritdoc
96
- parent_cls = @relations[m[:owner]].parent
97
- mixins = @relations[m[:owner]].mixins
98
-
99
- # Warn when no parent or mixins at all
100
- if !parent_cls && mixins.length == 0
101
- warn("parent class not found", m) unless m[:autodetected]
102
- return nil
103
- end
104
-
105
- # First check for the member in all mixins, because members
106
- # from mixins override those from parent class. Looking first
107
- # from mixins is probably a bit slower, but it's the correct
108
- # order to do things.
109
- if mixins.length > 0
110
- parent = mixins.map {|mix| lookup_member(mix, m) }.compact.first
111
- end
112
-
113
- # When not found, try looking from parent class
114
- if !parent && parent_cls
115
- parent = lookup_member(parent_cls, m)
116
- end
117
-
118
- # Only when both parent and mixins fail, throw warning
119
- if !parent
120
- warn("parent member not found", m) unless m[:autodetected]
121
- return nil
122
- end
123
- end
124
-
125
- return parent[:inheritdoc] ? find_parent(parent) : parent
126
- end
127
-
128
- def lookup_member(cls, m)
129
- inherit = m[:inheritdoc] || {}
130
- name = inherit[:member] || m[:name]
131
- tagname = inherit[:type] || m[:tagname]
132
- static = inherit[:static] || m[:static]
133
-
134
- if m[:autodetected]
135
- # Auto-detected properties can override either a property or a
136
- # config. So look for both types.
137
- if tagname == :property
138
- cfg = cls.find_members(:name => name, :tagname => :cfg, :static => static || false)[0]
139
- prop = cls.find_members(:name => name, :tagname => :property, :static => static || false)[0]
140
-
141
- if cfg && prop
142
- prop
143
- elsif cfg
144
- cfg
145
- elsif prop
146
- prop
147
- else
148
- nil
149
- end
150
-
151
- else
152
- # Unless the auto-detected member is detected as static,
153
- # look only at instance members.
154
- cls.find_members(:name => name, :tagname => tagname, :static => static || false)[0]
155
- end
156
- else
157
- cls.find_members(:name => name, :tagname => tagname, :static => static)[0]
20
+ @inherit_class.resolve(cls)
21
+ @inherit_members.resolve(cls)
158
22
  end
159
23
  end
160
24
 
161
- # Copy over doc from parent class.
162
- def resolve_class(cls)
163
- parent = find_class_parent(cls)
164
-
165
- if parent
166
- cls[:doc] = (cls[:doc] + "\n\n" + parent[:doc]).strip
167
- end
168
- end
169
-
170
- def find_class_parent(cls)
171
- if cls[:inheritdoc][:cls]
172
- # @inheritdoc MyClass
173
- parent = @relations[cls[:inheritdoc][:cls]]
174
- return warn("class not found", cls) unless parent
175
- else
176
- # @inheritdoc
177
- parent = cls.parent
178
- return warn("parent class not found", cls) unless parent
179
- end
180
-
181
- return parent[:inheritdoc] ? find_class_parent(parent) : parent
182
- end
183
-
184
- def warn(msg, item)
185
- context = item[:files][0]
186
- i_member = item[:inheritdoc][:member]
187
-
188
- msg = "@inheritdoc #{item[:inheritdoc][:cls]}"+ (i_member ? "#" + i_member : "") + " - " + msg
189
- Logger.warn(:inheritdoc, msg, context[:filename], context[:linenr])
190
-
191
- return nil
192
- end
193
-
194
25
  end
195
26
 
196
27
  end
@@ -0,0 +1,257 @@
1
+ require 'jsduck/logger'
2
+ require 'jsduck/class'
3
+
4
+ module JsDuck
5
+ module Process
6
+
7
+ # Deals with inheriting member documentation.
8
+ class InheritMembers
9
+ def initialize(relations)
10
+ @relations = relations
11
+ end
12
+
13
+ # Inherits docs for all members in class.
14
+ #
15
+ # In case of members with explicit @inheritdoc tags we inherit
16
+ # the following fields when they're not empty in current member:
17
+ #
18
+ # - :doc
19
+ # - :params
20
+ # - :return
21
+ # - :throws
22
+ #
23
+ # In case of auto-detected members that inherit from a public
24
+ # member in parent class, we inherit all fields that aren't
25
+ # present in current member, plus the :type field.
26
+ #
27
+ # Auto-detected members inheriting from other private
28
+ # auto-detected members follow the same rules of inheritance as
29
+ # members with explicit @inheritdoc.
30
+ #
31
+ # Additionally auto-detected properties get turned into configs
32
+ # when a public configs with same name is detected in parent
33
+ # class.
34
+ #
35
+ def resolve(cls)
36
+ new_cfgs = []
37
+
38
+ cls.all_local_members.each do |member|
39
+ if member[:inheritdoc]
40
+ resolve_member(cls, member, new_cfgs)
41
+ end
42
+ end
43
+
44
+ move_cfgs(cls, new_cfgs) if new_cfgs.length > 0
45
+ end
46
+
47
+ private
48
+
49
+ def resolve_member(cls, m, new_cfgs)
50
+ parent = find_parent(m)
51
+ if parent && parent[:inheritdoc]
52
+ resolve_parent(cls, parent)
53
+ end
54
+
55
+ if m[:inheritdoc] && parent
56
+ if autodetected?(m) && !parent[:private]
57
+ auto_inherit(m, parent)
58
+ else
59
+ inherit(m, parent)
60
+ end
61
+
62
+ # remember properties that have changed to configs
63
+ if autodetected?(m) && m[:tagname] != parent[:tagname]
64
+ new_cfgs << m
65
+ end
66
+ end
67
+
68
+ resolve_visibility(m, parent)
69
+
70
+ m[:inheritdoc] = nil
71
+ end
72
+
73
+ def resolve_parent(cls, parent)
74
+ new_cfgs = []
75
+ resolve_member(cls, parent, new_cfgs)
76
+ move_cfgs(cls, new_cfgs) if new_cfgs.length > 0
77
+ end
78
+
79
+ def inherit(m, parent)
80
+ m[:doc] = parent[:doc] if m[:doc].empty?
81
+
82
+ m[:params] = parent[:params] if inherit_params?(m, parent)
83
+ m[:return] = parent[:return] unless m[:return]
84
+ m[:throws] = parent[:throws] unless m[:throws] && m[:throws].length > 0
85
+
86
+ # Don't inherit type from parent when:
87
+ # - member itself has type and it's not auto-detected
88
+ # - or the type in parent is auto-detected.
89
+ unless m[:type] && m[:type] != "Object" && !auto?(m, :type) || auto?(parent, :type)
90
+ m[:type] = parent[:type]
91
+ end
92
+ end
93
+
94
+ def inherit_params?(m, parent)
95
+ # ignore the auto-inserted param of Ext4-style events
96
+ params = (m[:params] || []).reject {|p| p[:ext4_auto_param] }
97
+
98
+ if params.length > 0 && !auto?(m, :params)
99
+ # member itself has params and these are not auto-detected
100
+ false
101
+ elsif auto?(parent, :params)
102
+ # Params in parent are auto-detected.
103
+ false
104
+ else
105
+ true
106
+ end
107
+ end
108
+
109
+ def auto_inherit(m, parent)
110
+ m[:doc] = parent[:doc] if m[:doc].empty?
111
+
112
+ parent.each_pair do |key, value|
113
+ if key == :type || !m[key]
114
+ m[key] = value
115
+ end
116
+ end
117
+ end
118
+
119
+ # True when specific field of member has been auto-detected
120
+ def auto?(m, key)
121
+ m[:autodetected] && m[:autodetected][key]
122
+ end
123
+
124
+ # Changes given properties into configs within class
125
+ def move_cfgs(cls, members)
126
+ members.each do |m|
127
+ m[:tagname] = :cfg
128
+ end
129
+ # Ask class to update its internal caches for these members
130
+ cls.update_members!(members)
131
+ end
132
+
133
+ # For auto-detected members/classes (which have @private == :inherit)
134
+ # Use the visibility from parent class (defaulting to private when no parent).
135
+ def resolve_visibility(m, parent)
136
+ if autodetected?(m) && !JsDuck::Class.constructor?(m)
137
+ if !parent || parent[:private]
138
+ m[:private] = true
139
+ end
140
+
141
+ m[:protected] = true if parent && parent[:protected]
142
+ end
143
+ end
144
+
145
+ # Finds parent member of the given member. When @inheritdoc names
146
+ # a member to inherit from, finds that member instead.
147
+ #
148
+ # If the parent also has @inheritdoc, continues recursively.
149
+ def find_parent(m)
150
+
151
+ inherit = m[:inheritdoc] || {}
152
+ if inherit[:cls]
153
+ # @inheritdoc MyClass#member
154
+ parent_cls = @relations[m[:inheritdoc][:cls]]
155
+ return warn("class not found", m) unless parent_cls
156
+
157
+ parent = lookup_member(parent_cls, m)
158
+ return warn("member not found", m) unless parent
159
+
160
+ elsif inherit[:member]
161
+ # @inheritdoc #member
162
+ parent = lookup_member(@relations[m[:owner]], m)
163
+ return warn("member not found", m) unless parent
164
+
165
+ else
166
+ # @inheritdoc
167
+ parent_cls = @relations[m[:owner]].parent
168
+ mixins = @relations[m[:owner]].mixins
169
+
170
+ # Warn when no parent or mixins at all
171
+ if !parent_cls && mixins.length == 0
172
+ warn("parent class not found", m) unless autodetected?(m)
173
+ return nil
174
+ end
175
+
176
+ # First check for the member in all mixins, because members
177
+ # from mixins override those from parent class. Looking first
178
+ # from mixins is probably a bit slower, but it's the correct
179
+ # order to do things.
180
+ if mixins.length > 0
181
+ parent = mixins.map {|mix| lookup_member(mix, m) }.compact.first
182
+ end
183
+
184
+ # When not found, try looking from parent class
185
+ if !parent && parent_cls
186
+ parent = lookup_member(parent_cls, m)
187
+ end
188
+
189
+ # Only when both parent and mixins fail, throw warning
190
+ if !parent
191
+ warn("parent member not found", m) unless autodetected?(m)
192
+ return nil
193
+ end
194
+ end
195
+
196
+ return parent
197
+ end
198
+
199
+ def lookup_member(cls, m)
200
+ inherit = m[:inheritdoc] || {}
201
+ name = inherit[:member] || m[:name]
202
+ tagname = inherit[:type] || m[:tagname]
203
+ # When not explicitly inheriting from static member
204
+ # and the member itself is not static,
205
+ # inherit from instance member.
206
+ static = inherit[:static] || m[:static] || false
207
+
208
+ if autodetected?(m)
209
+ # Auto-detected properties can override either a property or a
210
+ # config. So look for both types.
211
+ if tagname == :property
212
+ cfg = cls.find_members(:name => name, :tagname => :cfg, :static => static)[0]
213
+ prop = cls.find_members(:name => name, :tagname => :property, :static => static)[0]
214
+
215
+ if cfg && prop
216
+ prop
217
+ elsif cfg
218
+ cfg
219
+ elsif prop
220
+ prop
221
+ else
222
+ nil
223
+ end
224
+
225
+ else
226
+ cls.find_members(:name => name, :tagname => tagname, :static => static)[0]
227
+ end
228
+ else
229
+ m = cls.find_members(:name => name, :tagname => tagname, :static => static)[0]
230
+ # When member was not found with explicit staticality and
231
+ # the @inheritdoc tag contained no explicit "static", then
232
+ # look for both static and instance members.
233
+ if !m && !inherit[:static]
234
+ m = cls.find_members(:name => name, :tagname => tagname, :static => nil)[0]
235
+ end
236
+ m
237
+ end
238
+ end
239
+
240
+ # True when the entire member was auto-detected
241
+ def autodetected?(m)
242
+ m[:autodetected] && m[:autodetected][:tagname]
243
+ end
244
+
245
+ def warn(msg, m)
246
+ inh_member = m[:inheritdoc][:member]
247
+ inh_target = (m[:inheritdoc][:cls] || "") + (inh_member ? "#" + inh_member : "")
248
+
249
+ Logger.warn(:inheritdoc, "@inheritdoc #{inh_target} - #{msg}", m[:files][0])
250
+
251
+ return nil
252
+ end
253
+
254
+ end
255
+
256
+ end
257
+ end