Commit 874e170f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Handling multiple definitions of one class.

Reverse-engineered the behaviour of ext-doc when merging multiple
doc-blocks for the same class.  Not 100% the same, but mostly.

Now finally we have support for most of the stuff that ext-doc does
on parsing-side.  The output-side needs quite a bit more effort,
but parsing and analyzing of input seems almost complete now.
parent 8592e6ab
Loading
Loading
Loading
Loading
+41 −15
Original line number Diff line number Diff line
@@ -25,25 +25,51 @@ module JsDuck
      end
    end

    # Registers documentation node.
    #
    # For each :class we create new node.  Other things we try to
    # place into classes where they belong.
    # Registers documentation node either as class or as member of
    # some class.
    def register(node)
      if node[:tagname] == :class
        add_class(node)
      else
        add_member(node)
      end
    end

    # When class exists, merge it with class node.
    # Otherwise add as new class.
    def add_class(cls)
      old_cls = @classes[cls[:name]]
      if old_cls
        merge_classes(old_cls, cls)
        @current_class = old_cls
      else
        @current_class = cls
        @documentation << cls
        @classes[cls[:name]] = cls
        insert_orphans(cls)
      end
    end

    # Merges new class-doc into old one.
    def merge_classes(old, new)
      [:extends, :xtype, :singleton, :private].each do |tag|
        old[tag] = old[tag] || new[tag]
      end
      old[:doc] = old[:doc].length > 0 ? old[:doc] : new[:doc]
      old[:cfg] = old[:cfg] + new[:cfg]
    end

    # Tries to place members into classes where they belong.
    #
    # @member explicitly defines that containing class, but we can
    # meet entity with member=Foo before we actually meet class Foo -
    # in that case we register them as orphans.  (Later when we
    # finally meet class Foo, orphans are inserted into it.)
    # @member explicitly defines the containing class, but we can meet
    # item with @member=Foo before we actually meet class Foo - in
    # that case we register them as orphans.  (Later when we finally
    # meet class Foo, orphans are inserted into it.)
    #
    # Items without @member belong by default to the preceding class.
    # When no class precedes them - they too are orphaned.
    def register(node)
      if node[:tagname] == :class
        @current_class = node
        @documentation << node
        @classes[node[:name]] = node
        insert_orphans(node)
      elsif node[:member]
    def add_member(node)
      if node[:member]
        if @classes[node[:member]]
          @classes[node[:member]][node[:tagname]] << node
        else
+86 −13
Original line number Diff line number Diff line
require "jsduck/aggregator"

describe JsDuck::Aggregator, "@member" do
describe JsDuck::Aggregator do

  def parse(string)
    agr = JsDuck::Aggregator.new
@@ -8,6 +8,8 @@ describe JsDuck::Aggregator, "@member" do
    agr.result
  end

  describe "@member" do

    it "defines the class where item belongs" do
      items = parse("
/**
@@ -47,5 +49,76 @@ describe JsDuck::Aggregator, "@member" do
")
      items[0][:cfg].length.should == 1
    end
  end

  describe "one class many times" do
    before do
      @classes = parse("
/**
 * @class Foo
 * @cfg c1
 */
  /** @method fun1 */
  /** @event eve1 */
  /** @property prop1 */
/**
 * @class Foo
 * @extends Bar
 * Second description.
 * @xtype xfoo
 * @private
 * @cfg c2
 */
  /** @method fun2 */
  /** @event eve3 */
  /** @property prop2 */
/**
 * @class Foo
 * @extends Bazaar
 * @singleton
 * Third description.
 * @xtype xxxfoo
 * @cfg c3
 */
  /** @method fun3 */
  /** @event eve3 */
  /** @property prop3 */
")
    end

    it "results in only one class" do
      @classes.length.should == 1
    end

    it "takes class doc from first doc-block that has one" do
      @classes[0][:doc].should == "Second description."
    end

    it "takes @extends from first doc-block that has one" do
      @classes[0][:extends].should == "Bar"
    end

    it "takes @xtype from first doc-block that has one" do
      @classes[0][:xtype].should == "xfoo"
    end

    it "is singleton when one doc-block is singleton" do
      @classes[0][:singleton].should == true
    end

    it "is private when one doc-block is private" do
      @classes[0][:private].should == true
    end

    it "combines all configs" do
      @classes[0][:cfg].length.should == 3
    end

    it "combines all methods, events, properties" do
      @classes[0][:method].length.should == 3
      @classes[0][:event].length.should == 3
      @classes[0][:property].length.should == 3
    end
  end

end