Commit 1915fdb1 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Move Markdown formatting classes to Format namespace.

parent c37f8fc5
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ require 'jsduck/guides'
require 'jsduck/videos'
require 'jsduck/examples'
require 'jsduck/categories/factory'
require 'jsduck/doc_formatter'
require 'jsduck/format/doc'

module JsDuck

@@ -27,7 +27,7 @@ module JsDuck
      @relations = relations
      @opts = opts

      doc_formatter = DocFormatter.new(@relations, @opts)
      doc_formatter = Format::Doc.new(@relations, @opts)

      @images = Img::DirSet.new(@opts.images, "images")
      @welcome = Welcome.create(@opts.welcome, doc_formatter)

lib/jsduck/batch_formatter.rb

deleted100644 → 0
+0 −60
Original line number Diff line number Diff line
require 'jsduck/util/parallel'
require 'jsduck/class_formatter'
require 'jsduck/doc_formatter'
require 'jsduck/img/dir_set'
require 'jsduck/logger'

module JsDuck

  # Performs the formatting of the doc-object of all classes.
  class BatchFormatter

    # Formats all classes.
    # Also registers found images in assets.
    def self.format_all!(relations, assets, opts)
      # Format all doc-objects in parallel
      formatted_classes = Util::Parallel.map(relations.classes) do |cls|

        files = cls[:files].map {|f| f[:filename] }.join(" ")
        Logger.log("Markdown formatting #{cls[:name]}", files)

        formatter = create_class_formatter(relations, opts)
        begin
          {
            :doc => formatter.format(cls.internal_doc),
            :images => formatter.images.all_used
          }
        rescue
          Logger.fatal_backtrace("Error while formatting #{cls[:name]} #{files}", $!)
          exit(1)
        end
      end

      # Then merge the data back to classes sequentially
      formatted_classes.each do |cls|
        relations[cls[:doc][:name]].internal_doc = cls[:doc]
        # Perform lookup of all the images again.  We're really doing
        # this work twice now, but as we usually don't have excessive
        # amounts of images, the performance penalty should be minimal.
        cls[:images].each {|img| assets.images.get(img[:filename]) }
      end

      # Print warnings for unused images
      assets.images.report_unused
    end

    # Factory method to create new ClassFormatter instances.
    def self.create_class_formatter(relations, opts)
      doc_formatter = DocFormatter.new(relations, opts)
      doc_formatter.images = Img::DirSet.new(opts.images, "images")

      class_formatter = ClassFormatter.new(relations, doc_formatter)
      # Don't format types when exporting
      class_formatter.include_types = !opts.export

      class_formatter
    end

  end

end

lib/jsduck/class_formatter.rb

deleted100644 → 0
+0 −94
Original line number Diff line number Diff line
require 'jsduck/type_parser'
require 'jsduck/logger'
require 'jsduck/tag_registry'
require 'jsduck/shortener'
require 'jsduck/util/html'

module JsDuck

  # Converts :doc properties of class from markdown to HTML, resolves
  # @links, and converts type definitions to HTML.
  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[:files][0]
      format_tags(cls)
      # format all members (except hidden ones)
      cls[:members] = cls[:members].map {|m| m[:hide] ? m : format_member(m)  }
      cls
    end

    # Access to the Img::DirSet object inside doc-formatter
    def images
      @formatter.images
    end

    private

    def format_member(m)
      @formatter.doc_context = m[:files][0]
      format_tags(m)
      if expandable?(m) || Shortener.too_long?(m[:doc])
        m[:shortDoc] = Shortener.shorten(m[:doc])
      end

      # We don't validate and format CSS var and mixin type definitions
      is_css_tag = m[:tagname] == :css_var || m[:tagname] == :css_mixin

      m[:html_type] = (@include_types && !is_css_tag) ? format_type(m[:type]) : m[:type] if m[:type]
      m[:params] = m[:params].map {|p| format_item(p, is_css_tag) } if m[:params]
      m[:return] = format_item(m[:return], is_css_tag) if m[:return]
      m[:throws] = m[:throws].map {|t| format_item(t, is_css_tag) } if m[:throws]
      m[:properties] = m[:properties].map {|b| format_item(b, is_css_tag) } if m[:properties]
      m
    end

    def expandable?(m)
      m[:params] || (m[:properties] && m[:properties].length > 0) || m[:default] || m[:deprecated] || m[:template]
    end

    def format_item(it, is_css_tag)
      it[:doc] = @formatter.format(it[:doc]) if it[:doc]
      it[:html_type] = (@include_types && !is_css_tag) ? format_type(it[:type]) : it[:type] if it[:type]
      it[:properties] = it[:properties].map {|s| format_item(s, is_css_tag) } if it[:properties]
      it
    end

    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.warn(:type_syntax, "Incorrect type syntax #{type}", context[:filename], context[:linenr])
        else
          Logger.warn(:type_name, "Unknown type #{type}", context[:filename], context[:linenr])
        end
        Util::HTML.escape(type)
      end
    end

    def format_tags(context)
      TagRegistry.html_renderers.each do |tag|
        if context[tag.key]
          tag.format(context, @formatter)
        end
      end
    end

  end

end

lib/jsduck/doc_formatter.rb

deleted100644 → 0
+0 −141
Original line number Diff line number Diff line
require 'rubygems'
require 'strscan'
require 'rdiscount'
require 'jsduck/html_stack'
require 'jsduck/inline/link'
require 'jsduck/inline/auto_link'
require 'jsduck/inline/link_renderer'
require 'jsduck/inline/img'
require 'jsduck/inline/video'
require 'jsduck/inline/example'

module JsDuck

  # Formats doc-comments
  class DocFormatter

    # Creates a formatter configured with options originating from
    # command line.  For the actual effect of the options see
    # Inline::* classes.
    def initialize(relations={}, opts={})
      @opts = opts
      @link_renderer = Inline::LinkRenderer.new(relations, opts)
      @inline_link = Inline::Link.new(@link_renderer)
      @auto_link = Inline::AutoLink.new(@link_renderer)
      @inline_img = Inline::Img.new(opts)
      @inline_video = Inline::Video.new(opts)
      @inline_example = Inline::Example.new(opts)
      @doc_context = {}
    end

    # Accessors to the images attribute of Inline::Img
    def images=(images)
      @inline_img.images = images
    end
    def images
      @inline_img.images
    end

    # Sets up instance to work in context of particular class, so
    # that when {@link #blah} is encountered it knows that
    # Context#blah is meant.
    def class_context=(cls)
      @inline_link.class_context = cls
      @auto_link.class_context = cls
    end

    # Sets up instance to work in context of particular doc object.
    # Used for error reporting.
    def doc_context=(doc)
      @doc_context = doc
      @inline_video.doc_context = doc
      @inline_link.doc_context = doc
      @auto_link.doc_context = doc
      @inline_img.doc_context = doc
    end

    # Returns the current documentation context
    def doc_context
      @doc_context
    end

    # Formats doc-comment for placement into HTML.
    # Renders it with Markdown-formatter and replaces @link-s.
    def format(input)
      # In ExtJS source "<pre>" is often at the end of paragraph, not
      # on its own line.  But in that case RDiscount doesn't recognize
      # it as the beginning of <pre>-block and goes on parsing it as
      # normal Markdown, which often causes nested <pre>-blocks.
      #
      # To prevent this, we always add extra newline before <pre>.
      input.gsub!(/([^\n])<pre>/, "\\1\n<pre>")

      # But we remove trailing newline after <pre> to prevent
      # code-blocks beginning with empty line.
      input.gsub!(/<pre>(<code>)?\n?/, "<pre>\\1")

      replace(RDiscount.new(input).to_html)
    end

    # Replaces {@link} and {@img} tags, auto-generates links for
    # recognized classnames.
    #
    # Replaces {@link Class#member link text} in given string with
    # HTML from @link_tpl.
    #
    # Replaces {@img path/to/image.jpg Alt text} with HTML from @img_tpl.
    #
    # Adds 'inline-example' class to code examples beginning with @example.
    #
    # Additionally replaces strings recognized as ClassNames or
    # #members with links to these classes or members.  So one doesn't
    # even need to use the @link tag to create a link.
    def replace(input)
      s = StringScanner.new(input)
      out = ""

      # Keep track of open HTML tags. We're not auto-detecting class
      # names when inside <a>. Also we want to close down the unclosed
      # tags.
      tags = HtmlStack.new(@opts[:ignore_html] || {}, @doc_context)

      while !s.eos? do
        if substitute = @inline_link.replace(s)
          out += substitute
        elsif substitute = @inline_img.replace(s)
          out += substitute
        elsif substitute = @inline_video.replace(s)
          out += substitute
        elsif s.check(/[{]/)
          # There might still be "{" that doesn't begin {@link} or {@img} - ignore it
          out += s.scan(/[{]/)
        elsif substitute = @inline_example.replace(s)
          out += substitute
        elsif s.check(/<\w/)
          # Open HTML tag
          out += s.scan(/</) + tags.open(s.scan(/\w+/)) + s.scan_until(/>|\z/)
        elsif s.check(/<\/\w+>/)
          # Close HTML tag
          out += s.scan(/<\//) + tags.close(s.scan(/\w+/)) + s.scan(/>/)
        elsif s.check(/</)
          # Ignore plain '<' char.
          out += s.scan(/</)
        else
          # Replace class names in the following text up to next "<" or "{"
          # but only when we're not inside <a>...</a>
          text = s.scan(/[^{<]+/)
          out += tags.open?("a") ? text : @auto_link.replace(text)
        end
      end

      out + tags.close_unfinished
    end

    # Creates a link based on the link template.
    def link(cls, member, anchor_text, type=nil, static=nil)
      @link_renderer.link(cls, member, anchor_text, type, static)
    end

  end

end
+2 −2
Original line number Diff line number Diff line
require 'jsduck/util/stdout'
require 'jsduck/exporter/full'
require 'jsduck/exporter/examples'
require 'jsduck/batch_formatter'
require 'jsduck/format/batch'
require 'jsduck/class_writer'
require 'jsduck/guide_writer'
require 'fileutils'
@@ -54,7 +54,7 @@ module JsDuck
    end

    def format_classes
      BatchFormatter.format_all!(@relations, @assets, @opts)
      Format::Batch.format_all!(@relations, @assets, @opts)
    end

  end
Loading