Commit 60e3d408 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Support for @member tag.

DocParser now recognizes @member and Merger sets it for class
members if it finds it.

A lot of changes to Aggregator to support it.  Also added rspec
to check that it all works as intended.
parent 4233195c
Loading
Loading
Loading
Loading
+50 −10
Original line number Diff line number Diff line
@@ -5,28 +5,68 @@ module JsDuck
  class Aggregator
    def initialize
      @documentation = []
      @classes = {}
      @orphans = []
      @current_class = nil
      @doc_parser = DocParser.new
      @merger = Merger.new
    end

    def parse(input)
      current_class = nil
      @current_class = nil
      Parser.new(input).parse.each do |docset|
        node = @merger.merge(@doc_parser.parse(docset[:comment]), docset[:code])
        # all methods, cfgs, ... following a class will be added to that class
        doc = @doc_parser.parse(docset[:comment])
        code = docset[:code]
        register(@merger.merge(doc, code))
      end
    end

    # Registers documentation node.
    #
    # For each :class we create new node.  Other things we try to
    # place 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.)
    #
    # 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
        @current_class = node
        @documentation << node
        elsif current_class
          current_class[ node[:tagname] ] << node
        @classes[node[:name]] = node
        insert_orphans(node)
      elsif node[:member]
        if @classes[node[:member]]
          @classes[node[:member]][node[:tagname]] << node
        else
          @documentation << node
          add_orphan(node)
        end
      elsif @current_class
        @current_class[ node[:tagname] ] << node
      else
        add_orphan(node)
      end
    end

    def add_orphan(node)
      @orphans << node
    end

    # Inserts available orphans to class
    def insert_orphans(cls)
      members = @orphans.find_all {|node| node[:member] == cls[:name] }
      members.each do |node|
        cls[node[:tagname]] << node
        @orphans.delete(node)
      end
    end

    def result
      @documentation
      @documentation + @orphans
    end
  end

+10 −0
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@ module JsDuck
          at_type
        elsif look(/@xtype\b/) then
          at_xtype
        elsif look(/@member\b/) then
          at_member
        elsif look(/@static\b/) then
          boolean_at_tag(/@static/, :static)
        elsif look(/@(private|ignore|hide|protected)\b/) then
@@ -179,6 +181,14 @@ module JsDuck
      skip_white
    end

    # matches @member name ...
    def at_member
      match(/@member/)
      add_tag(:member)
      maybe_ident_chain(:member)
      skip_white
    end

    # Used to match @private, @ignore, @hide, ...
    def boolean_at_tag(regex, propname)
      match(regex)
+12 −0
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ module JsDuck
      return {
        :tagname => :method,
        :name => detect_name(:method, doc_map, code),
        :member => detect_member(doc_map),
        :doc => detect_doc(docs),
        :params => detect_params(docs, code),
        :return => detect_return(doc_map),
@@ -127,6 +128,7 @@ module JsDuck
      return {
        :tagname => :event,
        :name => detect_name(:event, doc_map, code),
        :member => detect_member(doc_map),
        :doc => detect_doc(docs),
        :params => detect_params(docs, code),
        :private => !!doc_map[:private],
@@ -138,6 +140,7 @@ module JsDuck
      return {
        :tagname => :cfg,
        :name => detect_name(:cfg, doc_map, code),
        :member => detect_member(doc_map),
        :type => detect_type(:cfg, doc_map, code),
        :doc => detect_doc(docs),
        :private => !!doc_map[:private],
@@ -149,6 +152,7 @@ module JsDuck
      return {
        :tagname => :property,
        :name => detect_name(:property, doc_map, code),
        :member => detect_member(doc_map),
        :type => detect_type(:property, doc_map, code),
        :doc => detect_doc(docs),
        :private => !!doc_map[:private],
@@ -171,6 +175,14 @@ module JsDuck
      end
    end

    def detect_member(doc_map)
      if doc_map[:member]
        doc_map[:member].first[:member]
      else
        nil
      end
    end

    def detect_type(tagname, doc_map, code)
      main_tag = doc_map[tagname] ? doc_map[tagname].first : {}
      if main_tag[:type]
+55 −0
Original line number Diff line number Diff line
require 'jsduck/lexer'
require 'jsduck/parser'
require 'jsduck/doc_parser'
require 'jsduck/merger'
require "jsduck/aggregator"

describe JsDuck::Aggregator, "@member" do

  def parse(string)
    agr = JsDuck::Aggregator.new
    agr.parse(string)
    agr.result
  end

  it "defines the class where item belongs" do
    items = parse("
/**
 * @cfg foo
 * @member Bar
 */
")
    items[0][:member].should == "Bar"
  end

  it "forces item to be moved into that class" do
    items = parse("
/**
 * @class Bar
 */
/**
 * @class Baz
 */
/**
 * @cfg foo
 * @member Bar
 */
")
    items[0][:cfg].length.should == 1
    items[1][:cfg].length.should == 0
  end

  it "even when @member comes before the class itself" do
    items = parse("
/**
 * @cfg foo
 * @member Bar
 */
/**
 * @class Bar
 */
")
    items[0][:cfg].length.should == 1
  end

end