Class: JsDuck::Merger
- Inherits:
-
Object
- Object
- JsDuck::Merger
- Defined in:
- lib/jsduck/merger.rb
Overview
Takes data from doc-comment and code that follows it and combines these to pieces of information into one. The code comes from JsDuck::Parser and doc-comment from JsDuck::DocParser.
The main method merge() produces a hash as a result.
Instance Attribute Summary collapse
-
#filename ⇒ Object
Allow passing in filename and line for error reporting.
-
#linenr ⇒ Object
Returns the value of attribute linenr.
Instance Method Summary collapse
-
#add_shared(hash, doc_map) ⇒ Object
Detects properties common for each doc-object and adds them.
-
#build_aliases_hash(aliases, hash = {}) ⇒ Object
Given array of full alias names like “foo.bar”, “foo.baz” build hash like => [“bar”, “baz”].
-
#build_doc_map(docs) ⇒ Object
Build map of at-tags for quick lookup.
-
#class_name?(*name_chain) ⇒ Boolean
Class name begins with upcase char.
-
#code_matches_doc?(tagname, doc_map, code) ⇒ Boolean
True if the name detected from code matches with explicitly documented name.
- #combine_properties(raw_items) ⇒ Object
- #create_bare_class(docs, code) ⇒ Object
- #create_cfg(docs, code, owner = nil) ⇒ Object
- #create_class(docs, code) ⇒ Object
- #create_class_members(groups, owner) ⇒ Object
- #create_css_mixin(docs, code) ⇒ Object
- #create_css_var(docs, code) ⇒ Object
- #create_event(docs, code) ⇒ Object
- #create_member_id(m) ⇒ Object
- #create_method(docs, code) ⇒ Object
- #create_property(docs, code) ⇒ Object
- #detect_aliases(doc_map, code) ⇒ Object
- #detect_default(tagname, doc_map, code) ⇒ Object
-
#detect_doc(docs) ⇒ Object
Combines :doc-s of most tags Ignores tags that have doc comment themselves and subproperty tags.
-
#detect_doc_type(docs, code) ⇒ Object
Detects whether the doc-comment is for class, cfg, event, method or property.
- #detect_explicit_params(doc_map) ⇒ Object
- #detect_extends(doc_map, code) ⇒ Object
- #detect_implicit_params(code) ⇒ Object
-
#detect_list(type, doc_map, code) ⇒ Object
for detecting mixins and alternateClassNames.
- #detect_meta(doc_map) ⇒ Object
- #detect_name(tagname, doc_map, code, name_type = :last_name) ⇒ Object
- #detect_owner(doc_map) ⇒ Object
- #detect_params(tagname, doc_map, code) ⇒ Object
- #detect_required(doc_map) ⇒ Object
- #detect_return(doc_map, default_type = "undefined") ⇒ Object
- #detect_singleton(doc_map, code) ⇒ Object
- #detect_subproperties(docs, tagname) ⇒ Object
- #detect_type(tagname, doc_map, code) ⇒ Object
-
#group_class_docs(docs) ⇒ Object
Gathers all tags until first @cfg or @constructor into the first bare :class group.
-
#initialize ⇒ Merger
constructor
A new instance of Merger.
- #merge(docs, code) ⇒ Object
- #subproperty?(tag) ⇒ Boolean
Constructor Details
#initialize ⇒ Merger
Returns a new instance of Merger.
16 17 18 19 20 |
# File 'lib/jsduck/merger.rb', line 16 def initialize @filename = "" @linenr = 0 @meta_tags = MetaTagRegistry.instance end |
Instance Attribute Details
#filename ⇒ Object
Allow passing in filename and line for error reporting
13 14 15 |
# File 'lib/jsduck/merger.rb', line 13 def filename @filename end |
#linenr ⇒ Object
Returns the value of attribute linenr.
14 15 16 |
# File 'lib/jsduck/merger.rb', line 14 def linenr @linenr end |
Instance Method Details
#add_shared(hash, doc_map) ⇒ Object
Detects properties common for each doc-object and adds them
228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/jsduck/merger.rb', line 228 def add_shared(hash, doc_map) hash.merge!({ :inheritable => !!doc_map[:inheritable], :inheritdoc => doc_map[:inheritdoc] ? doc_map[:inheritdoc].first : nil, :meta => (doc_map), }) # copy :private also to main hash hash[:private] = true if hash[:meta][:private] hash[:id] = create_member_id(hash) return hash end |
#build_aliases_hash(aliases, hash = {}) ⇒ Object
Given array of full alias names like “foo.bar”, “foo.baz” build hash like => [“bar”, “baz”]
When hash given as second argument, then merges the aliases into it instead of creating a new hash.
354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/jsduck/merger.rb', line 354 def build_aliases_hash(aliases, hash={}) aliases.each do |a| if a =~ /^([^.]+)\.(.+)$/ if hash[$1] hash[$1] << $2 else hash[$1] = [$2] end end end hash end |
#build_doc_map(docs) ⇒ Object
Build map of at-tags for quick lookup
486 487 488 489 490 491 492 493 494 495 496 |
# File 'lib/jsduck/merger.rb', line 486 def build_doc_map(docs) map = {} docs.each do |tag| if map[tag[:tagname]] map[tag[:tagname]] << tag else map[tag[:tagname]] = [tag] end end map end |
#class_name?(*name_chain) ⇒ Boolean
Class name begins with upcase char
80 81 82 |
# File 'lib/jsduck/merger.rb', line 80 def class_name?(*name_chain) return name_chain.last =~ /\A[A-Z]/ end |
#code_matches_doc?(tagname, doc_map, code) ⇒ Boolean
True if the name detected from code matches with explicitly documented name. Also true when no explicit name documented.
319 320 321 322 323 |
# File 'lib/jsduck/merger.rb', line 319 def code_matches_doc?(tagname, doc_map, code) explicit_name = detect_name(tagname, doc_map, {}) implicit_name = detect_name(tagname, {}, code) return explicit_name == "" || explicit_name == implicit_name end |
#combine_properties(raw_items) ⇒ Object
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/jsduck/merger.rb', line 432 def combine_properties(raw_items) # First item can't be namespaced, if it is ignore the rest. if raw_items[0] && raw_items[0][:name] =~ /\./ return [raw_items[0]] end # build name-index of all items index = {} raw_items.each {|it| index[it[:name]] = it } # If item name has no dots, add it directly to items array. # Otherwise look up the parent of item and add it as the # property of that parent. items = [] raw_items.each do |it| if it[:name] =~ /^(.+)\.([^.]+)$/ it[:name] = $2 parent = index[$1] if parent parent[:properties] = [] unless parent[:properties] parent[:properties] << it else Logger.instance.warn(:subproperty, "Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'.", @filename, @linenr) end else items << it end end items end |
#create_bare_class(docs, code) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/jsduck/merger.rb', line 124 def (docs, code) doc_map = build_doc_map(docs) return add_shared({ :tagname => :class, :name => detect_name(:class, doc_map, code, :full_name), :doc => detect_doc(docs), :extends => detect_extends(doc_map, code), :mixins => detect_list(:mixins, doc_map, code), :alternateClassNames => detect_list(:alternateClassNames, doc_map, code), :aliases => detect_aliases(doc_map, code), :singleton => detect_singleton(doc_map, code), :requires => detect_list(:requires, doc_map, code), :uses => detect_list(:uses, doc_map, code), # Used by Aggregator to determine if we're dealing with Ext4 code :code_type => code[:type], }, doc_map) end |
#create_cfg(docs, code, owner = nil) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/jsduck/merger.rb', line 177 def create_cfg(docs, code, owner = nil) doc_map = build_doc_map(docs) return add_shared({ :tagname => :cfg, :name => detect_name(:cfg, doc_map, code), :owner => detect_owner(doc_map) || owner, :type => detect_type(:cfg, doc_map, code), :doc => detect_doc(docs), :default => detect_default(:cfg, doc_map, code), :properties => detect_subproperties(docs, :cfg), :accessor => !!doc_map[:accessor], :evented => !!doc_map[:evented], }, doc_map) end |
#create_class(docs, code) ⇒ Object
84 85 86 87 88 89 90 |
# File 'lib/jsduck/merger.rb', line 84 def create_class(docs, code) groups = group_class_docs(docs) result = (groups[:class], code) result[:members] = create_class_members(groups, result[:name]) result[:statics] = Class.default_members_hash result end |
#create_class_members(groups, owner) ⇒ Object
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/jsduck/merger.rb', line 142 def create_class_members(groups, owner) members = Class.default_members_hash members[:cfg] = groups[:cfg].map { || create_cfg(, {}, owner) } if groups[:constructor].length > 0 constr = create_method(groups[:constructor], {}) constr[:owner] = owner members[:method] << constr end members end |
#create_css_mixin(docs, code) ⇒ Object
216 217 218 219 220 221 222 223 224 225 |
# File 'lib/jsduck/merger.rb', line 216 def create_css_mixin(docs, code) doc_map = build_doc_map(docs) return add_shared({ :tagname => :css_mixin, :name => detect_name(:css_mixin, doc_map, code), :owner => detect_owner(doc_map), :doc => detect_doc(docs), :params => detect_params(:css_mixin, doc_map, code), }, doc_map) end |
#create_css_var(docs, code) ⇒ Object
205 206 207 208 209 210 211 212 213 214 |
# File 'lib/jsduck/merger.rb', line 205 def create_css_var(docs, code) doc_map = build_doc_map(docs) return add_shared({ :tagname => :css_var, :name => detect_name(:css_var, doc_map, code), :owner => detect_owner(doc_map), :type => detect_type(:css_var, doc_map, code), :doc => detect_doc(docs), }, doc_map) end |
#create_event(docs, code) ⇒ Object
166 167 168 169 170 171 172 173 174 175 |
# File 'lib/jsduck/merger.rb', line 166 def create_event(docs, code) doc_map = build_doc_map(docs) return add_shared({ :tagname => :event, :name => detect_name(:event, doc_map, code), :owner => detect_owner(doc_map), :doc => detect_doc(docs), :params => detect_params(:event, doc_map, code), }, doc_map) end |
#create_member_id(m) ⇒ Object
242 243 244 245 246 |
# File 'lib/jsduck/merger.rb', line 242 def create_member_id(m) # Sanitize $ in member names with something safer name = m[:name].gsub(/\$/, 'S-') "#{m[:meta][:static] ? 'static-' : ''}#{m[:tagname]}-#{name}" end |
#create_method(docs, code) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/jsduck/merger.rb', line 153 def create_method(docs, code) doc_map = build_doc_map(docs) name = detect_name(:method, doc_map, code) return add_shared({ :tagname => :method, :name => name, :owner => detect_owner(doc_map), :doc => detect_doc(docs), :params => detect_params(:method, doc_map, code), :return => detect_return(doc_map, name == "constructor" ? "Object" : "undefined"), }, doc_map) end |
#create_property(docs, code) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/jsduck/merger.rb', line 192 def create_property(docs, code) doc_map = build_doc_map(docs) return add_shared({ :tagname => :property, :name => detect_name(:property, doc_map, code), :owner => detect_owner(doc_map), :type => detect_type(:property, doc_map, code), :doc => detect_doc(docs), :default => detect_default(:property, doc_map, code), :properties => detect_subproperties(docs, :property), }, doc_map) end |
#detect_aliases(doc_map, code) ⇒ Object
336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/jsduck/merger.rb', line 336 def detect_aliases(doc_map, code) if doc_map[:alias] build_aliases_hash(doc_map[:alias].map {|tag| tag[:name] }) elsif code[:xtype] || code[:alias] hash = {} build_aliases_hash(code[:xtype].map {|xtype| "widget."+xtype }, hash) if code[:xtype] build_aliases_hash(code[:alias], hash) if code[:alias] hash else {} end end |
#detect_default(tagname, doc_map, code) ⇒ Object
308 309 310 311 312 313 314 315 |
# File 'lib/jsduck/merger.rb', line 308 def detect_default(tagname, doc_map, code) main_tag = doc_map[tagname] ? doc_map[tagname].first : {} if main_tag[:default] main_tag[:default] elsif code_matches_doc?(tagname, doc_map, code) && code[:type] == :assignment && code[:right] code[:right][:value] end end |
#detect_doc(docs) ⇒ Object
Combines :doc-s of most tags Ignores tags that have doc comment themselves and subproperty tags
475 476 477 478 479 |
# File 'lib/jsduck/merger.rb', line 475 def detect_doc(docs) = [:param, :return, :meta] = docs.find_all { |tag| !.include?(tag[:tagname]) && !subproperty?(tag) } .map { |tag| tag[:doc] }.compact.join(" ") end |
#detect_doc_type(docs, code) ⇒ Object
Detects whether the doc-comment is for class, cfg, event, method or property.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/jsduck/merger.rb', line 42 def detect_doc_type(docs, code) doc_map = build_doc_map(docs) if doc_map[:class] :class elsif doc_map[:event] :event elsif doc_map[:method] :method elsif doc_map[:property] || doc_map[:type] :property elsif doc_map[:css_var] :css_var elsif doc_map[:cfg] && doc_map[:cfg].length == 1 # When just one @cfg, avoid treating it as @class :cfg elsif code[:type] == :ext_define :class elsif code[:type] == :assignment && class_name?(*code[:left]) :class elsif code[:type] == :function && class_name?(code[:name]) :class elsif code[:type] == :css_mixin :css_mixin elsif doc_map[:cfg] :cfg elsif code[:type] == :function :method elsif code[:type] == :assignment && code[:right] && code[:right][:type] == :function :method elsif doc_map[:return] || doc_map[:param] :method else :property end end |
#detect_explicit_params(doc_map) ⇒ Object
423 424 425 |
# File 'lib/jsduck/merger.rb', line 423 def detect_explicit_params(doc_map) combine_properties(doc_map[:param] || []) end |
#detect_extends(doc_map, code) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/jsduck/merger.rb', line 293 def detect_extends(doc_map, code) if doc_map[:extends] cls = doc_map[:extends].first[:extends] elsif code[:type] == :assignment && code[:right] && code[:right][:type] == :ext_extend cls = code[:right][:extend].join(".") elsif code[:type] == :ext_define # Classes defined with Ext.define will automatically inherit from Ext.Base cls = code[:extend] || "Ext.Base" else cls = nil end # Ignore extending of the Object class cls == "Object" ? nil : cls end |
#detect_implicit_params(code) ⇒ Object
413 414 415 416 417 418 419 420 421 |
# File 'lib/jsduck/merger.rb', line 413 def detect_implicit_params(code) if code[:type] == :function code[:params] elsif code[:type] == :assignment && code[:right] && code[:right][:type] == :function code[:right][:params] else [] end end |
#detect_list(type, doc_map, code) ⇒ Object
for detecting mixins and alternateClassNames
326 327 328 329 330 331 332 333 334 |
# File 'lib/jsduck/merger.rb', line 326 def detect_list(type, doc_map, code) if doc_map[type] doc_map[type].map {|d| d[type] }.flatten elsif code[:type] == :ext_define && code[type] code[type] else [] end end |
#detect_meta(doc_map) ⇒ Object
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/jsduck/merger.rb', line 367 def (doc_map) = {} (doc_map[:meta] || []).map do |tag| [tag[:name]] = [] unless [tag[:name]] [tag[:name]] << tag[:doc] end .each_pair do |key, value| tag = @meta_tags[key] [key] = tag.to_value(tag.boolean ? true : value) end [:required] = true if detect_required(doc_map) end |
#detect_name(tagname, doc_map, code, name_type = :last_name) ⇒ Object
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/jsduck/merger.rb', line 248 def detect_name(tagname, doc_map, code, name_type = :last_name) main_tag = doc_map[tagname] ? doc_map[tagname].first : {} if main_tag[:name] main_tag[:name] elsif doc_map[:constructor] "constructor" elsif code[:type] == :function || code[:type] == :css_mixin code[:name] elsif code[:type] == :assignment name_type == :full_name ? code[:left].join(".") : code[:left].last elsif code[:type] == :ext_define name_type == :full_name ? code[:name] : code[:name].split(/\./).last else "" end end |
#detect_owner(doc_map) ⇒ Object
265 266 267 268 269 270 271 |
# File 'lib/jsduck/merger.rb', line 265 def detect_owner(doc_map) if doc_map[:member] doc_map[:member].first[:member] else nil end end |
#detect_params(tagname, doc_map, code) ⇒ Object
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/jsduck/merger.rb', line 391 def detect_params(tagname, doc_map, code) implicit = code_matches_doc?(tagname, doc_map, code) ? detect_implicit_params(code) : [] explicit = detect_explicit_params(doc_map) # Override implicit parameters with explicit ones # But if explicit ones exist, don't append the implicit ones. params = [] (explicit.length > 0 ? explicit.length : implicit.length).times do |i| im = implicit[i] || {} ex = explicit[i] || {} doc = ex[:doc] || im[:doc] || "" params << { :type => ex[:type] || im[:type] || "Object", :name => ex[:name] || im[:name] || "", :doc => doc, :optional => ex[:optional] || false, :default => ex[:default], :properties => ex[:properties] || [], } end params end |
#detect_required(doc_map) ⇒ Object
387 388 389 |
# File 'lib/jsduck/merger.rb', line 387 def detect_required(doc_map) doc_map[:cfg] && doc_map[:cfg].first[:optional] == false end |
#detect_return(doc_map, default_type = "undefined") ⇒ Object
463 464 465 466 467 468 469 470 471 |
# File 'lib/jsduck/merger.rb', line 463 def detect_return(doc_map, default_type="undefined") ret = doc_map[:return] ? doc_map[:return].first : {} return { :type => ret[:type] || default_type, :name => ret[:name] || "return", :doc => ret[:doc] || "", :properties => doc_map[:return] ? detect_subproperties(doc_map[:return], :return) : [] } end |
#detect_singleton(doc_map, code) ⇒ Object
383 384 385 |
# File 'lib/jsduck/merger.rb', line 383 def detect_singleton(doc_map, code) !!(doc_map[:singleton] || code[:type] == :ext_define && code[:singleton]) end |
#detect_subproperties(docs, tagname) ⇒ Object
427 428 429 430 |
# File 'lib/jsduck/merger.rb', line 427 def detect_subproperties(docs, tagname) prop_docs = docs.find_all {|tag| tag[:tagname] == tagname} prop_docs.length > 0 ? combine_properties(prop_docs)[0][:properties] : [] end |
#detect_type(tagname, doc_map, code) ⇒ Object
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/jsduck/merger.rb', line 273 def detect_type(tagname, doc_map, code) main_tag = doc_map[tagname] ? doc_map[tagname].first : {} if main_tag[:type] return main_tag[:type] elsif doc_map[:type] return doc_map[:type].first[:type] elsif code_matches_doc?(tagname, doc_map, code) if code[:type] == :function return "Function" elsif code[:type] == :assignment && code[:right] if code[:right][:type] == :function return "Function" elsif code[:right][:type] == :literal && code[:right][:class] != nil return code[:right][:class] end end end return "Object" end |
#group_class_docs(docs) ⇒ Object
Gathers all tags until first @cfg or @constructor into the first bare :class group. We have a special case for @xtype which in ExtJS comments often appears after @constructor - so we explicitly place it into :class group.
Then gathers each @cfg and tags following it into :cfg group, so that it becomes array of arrays of tags. This is to allow some configs to be marked with @private or whatever else.
Finally gathers tags after @constructor into its group.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/jsduck/merger.rb', line 102 def group_class_docs(docs) groups = {:class => [], :cfg => [], :constructor => []} group_name = :class docs.each do |tag| if tag[:tagname] == :cfg || tag[:tagname] == :constructor group_name = tag[:tagname] if tag[:tagname] == :cfg groups[:cfg] << [] end end if tag[:tagname] == :alias groups[:class] << tag elsif group_name == :cfg groups[:cfg].last << tag else groups[group_name] << tag end end groups end |
#merge(docs, code) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/jsduck/merger.rb', line 22 def merge(docs, code) case detect_doc_type(docs, code) when :class create_class(docs, code) when :event create_event(docs, code) when :method create_method(docs, code) when :cfg create_cfg(docs, code) when :property create_property(docs, code) when :css_var create_css_var(docs, code) when :css_mixin create_css_mixin(docs, code) end end |
#subproperty?(tag) ⇒ Boolean
481 482 483 |
# File 'lib/jsduck/merger.rb', line 481 def subproperty?(tag) (tag[:tagname] == :cfg || tag[:tagname] == :property) && tag[:name] =~ /\./ end |