Commit 5894631b authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Automatically add links inside type definitions.

When type definition names a known class, it's converted to link
into that class.
parent 5f780d05
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -152,6 +152,8 @@ module JsDuck
    # Formats each class
    def format_classes
      formatter = ClassFormatter.new(@relations, get_doc_formatter)
      # Don't format types when exporting
      formatter.include_types = !@opts.export
      # Format all doc-objects in parallel
      formatted_docs = @parallel.map(@relations.classes) do |cls|
        formatter.format(cls.internal_doc)
+32 −22
Original line number Diff line number Diff line
require 'jsduck/type_parser'
require 'jsduck/logger'

module JsDuck

  # Converts :doc properties of class from markdown to HTML and resolves @links.
  # Also removes private members.
  # Converts :doc properties of class from markdown to HTML, resolves
  # @links, and converts type definitions to HTML.  Also removes
  # private members.
  class ClassFormatter
    # Set to false to disable HTML-formatting of type definitions.
    attr_accessor :include_types

    def initialize(relations, formatter)
      @relations = relations
      @formatter = formatter
      @include_types = true
    end

    # Runs the formatter on doc object of a class.
    # Accessed using Class#internal_doc
    def format(cls)
      @cls = cls
      @formatter.class_context = cls[:name]
      @formatter.doc_context = cls
      cls[:doc] = @formatter.format(cls[:doc]) if cls[:doc]
@@ -30,31 +39,32 @@ module JsDuck
      if m[:params] || (m[:properties] && m[:properties].length > 0) || m[:default] || @formatter.too_long?(m[:doc])
        m[:shortDoc] = @formatter.shorten(m[:doc])
      end
      m[:params] = format_params(m[:params]) if m[:params]
      m[:return] = format_return(m[:return]) if m[:return]
      m[:properties] = format_subproperties(m[:properties]) if m[:properties]
      m[:html_type] = format_type(m[:type]) if m[:type] && @include_types
      m[:params] = m[:params].map {|p| format_item(p) } if m[:params]
      m[:return] = format_item(m[:return]) if m[:return]
      m[:properties] = m[:properties].map {|b| format_item(b) } if m[:properties]
      m
    end

    def format_params(params)
      params.map do |p|
        p[:doc] = @formatter.format(p[:doc]) if p[:doc]
        p[:properties] = format_subproperties(p[:properties]) if p[:properties]
        p
      end
    def format_item(it)
      it[:doc] = @formatter.format(it[:doc]) if it[:doc]
      it[:html_type] = format_type(it[:type]) if it[:type] && @include_types
      it[:properties] = it[:properties].map {|s| format_item(s) } if it[:properties]
      it
    end

    def format_return(r)
      r[:doc] = @formatter.format(r[:doc]) if r[:doc]
      r[:properties] = format_subproperties(r[:properties]) if r[:properties]
      r
    def format_type(type)
      tp = TypeParser.new(@relations, @formatter)
      if tp.parse(type)
        tp.out
      else
        context = @formatter.doc_context
        if tp.error == :syntax
          Logger.instance.warn("Incorrect type syntax #{type} in #{context[:filename]} line #{context[:linenr]}")
        else
          Logger.instance.warn("Unknown type #{type} in #{context[:filename]} line #{context[:linenr]}")
        end

    def format_subproperties(items)
      items.map do |it|
        it[:doc] = @formatter.format(it[:doc]) if it[:doc]
        it[:properties] = format_subproperties(it[:properties]) if it[:properties]
        it
        type
      end
    end

+0 −34
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/type_parser'

module JsDuck

@@ -17,7 +16,6 @@ module JsDuck
      warn_unnamed
      warn_optional_params
      warn_duplicate_params
      warn_types
    end

    # print warning for each global member
@@ -71,38 +69,6 @@ module JsDuck
      end
    end

    # Check parameter types
    def warn_types
      parser = TypeParser.new(@relations)
      each_member do |member|
        (member[:params] || []).each do |p|
          if !parser.parse(p[:type])
            if parser.error == :syntax
              warn("Incorrect parameter type syntax #{p[:type]}", member)
            else
              warn("Unknown parameter type #{p[:type]}", member)
            end
          end
        end

        if member[:return] && !parser.parse(member[:return][:type])
          if parser.error == :syntax
            warn("Incorrect return type syntax #{member[:return][:type]}", member)
          else
            warn("Unknown return type #{member[:return][:type]}", member)
          end
        end

        if member[:type] && !parser.parse(member[:type])
          if parser.error == :syntax
            warn("Incorrect type syntax #{member[:type]}", member)
          else
            warn("Unkown type #{member[:type]}", member)
          end
        end
      end
    end

    # Loops through all members of all classes
    def each_member(&block)
      @relations.each {|cls| cls.each_member(&block) }
+5 −5
Original line number Diff line number Diff line
@@ -182,12 +182,12 @@ module JsDuck
      end

      if m[:tagname] == :cfg || m[:tagname] == :property || m[:tagname] == :css_var
        params = "<span> : #{m[:type]}</span>"
        params = "<span> : #{m[:html_type]}</span>"
      else
        ps = m[:params].map {|p| render_short_param(p) }.join(", ")
        params = "( <span class='pre'>#{ps}</span> )"
        if m[:tagname] == :method && m[:return][:type] != "undefined"
          params += " : " + m[:return][:type]
          params += " : " + m[:return][:html_type]
        end
      end

@@ -216,7 +216,7 @@ module JsDuck
    end

    def render_short_param(param)
      p = param[:type] + " " + param[:name]
      p = param[:html_type] + " " + param[:name]
      return param[:optional] ? "["+p+"]" : p
    end

@@ -285,7 +285,7 @@ module JsDuck
      return [
        "<li>",
          "<span class='pre'>#{p[:name]}</span> : ",
          p[:type],
          p[:html_type],
          p[:optional] ? " (optional)" : "",
          "<div class='sub-desc'>",
            p[:doc],
@@ -301,7 +301,7 @@ module JsDuck
        "<h3 class='pa'>Returns</h3>",
        "<ul>",
          "<li>",
            "<span class='pre'>#{ret[:type]}</span>",
            "<span class='pre'>#{ret[:html_type]}</span>",
            "<div class='sub-desc'>",
              ret[:doc],
              ret[:properties] && ret[:properties].length > 0 ? render_params_and_return(ret) : "",
+25 −12
Original line number Diff line number Diff line
@@ -21,27 +21,36 @@ module JsDuck
    # - :name - one of the names of the types is unknown
    attr_reader :error

    # Initializes the parser with hash of valid type names
    def initialize(relations={})
    # When parsing was successful, then contains the output HTML - the
    # input type-definition with types themselves replaced with links.
    attr_reader :out

    # Initializes the parser with hash of valid type names and doc_formatter.
    def initialize(relations={}, formatter={})
      @relations = relations
      @formatter = formatter
    end

    def parse(str)
      @input = StringScanner.new(str)
      @error = :syntax
      @out = []

      # Return immediately if base type doesn't match
      return false unless base_type

      # Go through enumeration of types, separated with "/"
      while @input.check(/\//)
        @input.scan(/\//)
        @out << @input.scan(/\//)
        # Fail if there's no base type after "/"
        return false unless base_type
      end

      # The definition might end with an ellipsis
      @input.scan(/\.\.\./)
      @out << "..." if @input.scan(/\.\.\./)

      # Concatenate all output
      @out = @out.join

      # Success if we have reached the end of input
      return @input.eos?
@@ -53,18 +62,22 @@ module JsDuck
    #
    # dot-separated identifiers followed by optional "[]"
    def base_type
      type = @input.scan(/[a-zA-Z_]+(\.[a-zA-Z_]+)*(\[\])?/)
      return type && exists?(type)
    end
      type = @input.scan(/[a-zA-Z_]+(\.[a-zA-Z_]+)*/)

    def exists?(type)
      stype = type.sub(/\[\]$/, "")
      if @relations[stype] || @relations.ignore?(stype) || stype == "undefined"
        true
      if !type
        return false
      elsif @relations[type]
        @out << @formatter.link(type, nil, type)
      elsif @relations.ignore?(type) || type == "undefined"
        @out << type
      else
        @error = :name
        false
        return false
      end

      @out << "[]" if @input.scan(/\[\]/)

      true
    end

  end