Commit 1b4e11e7 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Merge branch 'master' into doc-tags

parents 457b9841 321539ee
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -27,8 +27,7 @@ module JsDuck
      @relations = relations
      @opts = opts

      doc_formatter = DocFormatter.new(@opts)
      doc_formatter.relations = @relations
      doc_formatter = DocFormatter.new(@relations, @opts)

      @images = Img::DirSet.new(@opts.images, "images")
      @welcome = Welcome.create(@opts.welcome, doc_formatter)
+1 −2
Original line number Diff line number Diff line
@@ -45,8 +45,7 @@ module JsDuck

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

      class_formatter = ClassFormatter.new(relations, doc_formatter)
+10 −13
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@ 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'
@@ -15,9 +17,11 @@ module JsDuck
    # Creates a formatter configured with options originating from
    # command line.  For the actual effect of the options see
    # Inline::* classes.
    def initialize(opts={})
    def initialize(relations={}, opts={})
      @opts = opts
      @inline_link = Inline::Link.new(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)
@@ -37,6 +41,7 @@ module JsDuck
    # 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.
@@ -45,6 +50,7 @@ module JsDuck
      @doc_context = doc
      @inline_video.doc_context = doc
      @inline_link.doc_context = doc
      @auto_link.doc_context = doc
      @inline_img.doc_context = doc
    end

@@ -53,15 +59,6 @@ module JsDuck
      @doc_context
    end

    # JsDuck::Relations for looking up class names.
    #
    # When auto-creating class links from CamelCased names found from
    # text, we check the relations object to see if a class with that
    # name actually exists.
    def relations=(relations)
      @inline_link.relations = relations
    end

    # Formats doc-comment for placement into HTML.
    # Renders it with Markdown-formatter and replaces @link-s.
    def format(input)
@@ -127,7 +124,7 @@ module JsDuck
          # 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 : @inline_link.create_magic_links(text)
          out += tags.open?("a") ? text : @auto_link.replace(text)
        end
      end

@@ -136,7 +133,7 @@ module JsDuck

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

  end
+106 −0
Original line number Diff line number Diff line
require 'jsduck/logger'

module JsDuck
  module Inline

    # Takes care of the auto-detection of links in text.
    class AutoLink
      # Sets up instance to work in context of particular class, so it
      # knows that #blah is in context of SomeClass.
      attr_accessor :class_context

      # Sets up instance to work in context of particular doc object.
      # Used for error reporting.
      attr_accessor :doc_context

      def initialize(link_renderer)
        @class_context = ""
        @doc_context = {}
        @relations = link_renderer.relations
        @renderer = link_renderer
        @magic_link_re = magic_link_re
      end

      # Looks input text for patterns like:
      #
      #  My.ClassName
      #  MyClass#method
      #  #someProperty
      #
      # and converts them to links, as if they were surrounded with
      # {@link} tag. One notable exception is that Foo is not created to
      # link, even when Foo class exists, but Foo.Bar is. This is to
      # avoid turning normal words into links. For example:
      #
      #     Math involves a lot of numbers. Ext JS is a JavaScript framework.
      #
      # In these sentences we don't want to link "Math" and "Ext" to the
      # corresponding JS classes.  And that's why we auto-link only
      # class names containing a dot "."
      #
      def replace(input)
        input.gsub(@magic_link_re) do
          cls = $1 || $3
          member = $2 || $4
          replace_magic_link(cls, member)
        end
      end

      private

      # Generates regex for auto-linking class and member names in text.
      def magic_link_re
        ident_re = "(?:[A-Za-z_$][A-Za-z0-9_$]*)"
        cls_re = "(#{ident_re}(?:\\.#{ident_re})*)"
        ns_cls_re = "(#{ident_re}(?:\\.#{ident_re})+)"
        member_re = "(?:#(#{ident_re}))"
        /#{cls_re}#{member_re}|#{ns_cls_re}|#{member_re}/m
      end

      def replace_magic_link(cls, member)
        if cls && member
          if @relations[cls] && @renderer.get_matching_member(cls, {:name => member})
            return @renderer.link(cls, member, cls+"."+member)
          else
            warn_magic_link("#{cls}##{member} links to non-existing " + (@relations[cls] ? "member" : "class"))
          end
        elsif cls
          if @relations[cls]
            return @renderer.link(cls, nil, cls)
          else
            cls2, member2 = split_to_cls_and_member(cls)
            if @relations[cls2] && @renderer.get_matching_member(cls2, {:name => member2})
              return @renderer.link(cls2, member2, cls2+"."+member2)
            elsif cls =~ /\.(js|css|html|php)\Z/
              # Ignore common filenames
            else
              warn_magic_link("#{cls} links to non-existing class")
            end
          end
        else
          if @renderer.get_matching_member(@class_context, {:name => member})
            return @renderer.link(@class_context, member, member)
          elsif member =~ /\A([A-F0-9]{3}|[A-F0-9]{6})\Z/i || member =~ /\A[0-9]/
            # Ignore HEX color codes and
            # member names beginning with number
          else
            warn_magic_link("##{member} links to non-existing member")
          end
        end

        return "#{cls}#{member ? '#' : ''}#{member}"
      end

      def split_to_cls_and_member(str)
        parts = str.split(/\./)
        return [parts.slice(0, parts.length-1).join("."), parts.last]
      end

      def warn_magic_link(msg)
        Logger.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
      end

    end

  end
end
+6 −132
Original line number Diff line number Diff line
require 'jsduck/util/html'
require 'jsduck/logger'
require 'jsduck/tag_registry'

@@ -19,28 +18,11 @@ module JsDuck
      # Used for error reporting.
      attr_accessor :doc_context

      # JsDuck::Relations for looking up class names.
      #
      # When auto-creating class links from CamelCased names found from
      # text, we check the relations object to see if a class with that
      # name actually exists.
      attr_accessor :relations

      def initialize(opts={})
      def initialize(link_renderer)
        @class_context = ""
        @doc_context = {}
        @relations = {}

        # Template HTML that replaces {@link Class#member anchor text}.
        # Can contain placeholders:
        #
        # %c - full class name (e.g. "Ext.Panel")
        # %m - class member name prefixed with member type (e.g. "method-urlEncode")
        # %# - inserts "#" if member name present
        # %- - inserts "-" if member name present
        # %a - anchor text for link
        @tpl = opts[:link_tpl] || '<a href="%c%#%m">%a</a>'

        @relations = link_renderer.relations
        @renderer = link_renderer
        @re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
      end

@@ -86,7 +68,7 @@ module JsDuck
          Logger.warn(:link, "#{full_link} links to non-existing class", file, line)
          return text
        elsif member
          ms = find_members(cls, {:name => member, :tagname => type, :static => static})
          ms = @renderer.find_members(cls, {:name => member, :tagname => type, :static => static})
          if ms.length == 0
            Logger.warn(:link, "#{full_link} links to non-existing member", file, line)
            return text
@@ -108,118 +90,10 @@ module JsDuck
            end
          end

          return link(cls, member, text, type, static)
          return @renderer.link(cls, member, text, type, static)
        else
          return link(cls, false, text)
        end
          return @renderer.link(cls, false, text)
        end

      # Looks input text for patterns like:
      #
      #  My.ClassName
      #  MyClass#method
      #  #someProperty
      #
      # and converts them to links, as if they were surrounded with
      # {@link} tag. One notable exception is that Foo is not created to
      # link, even when Foo class exists, but Foo.Bar is. This is to
      # avoid turning normal words into links. For example:
      #
      #     Math involves a lot of numbers. Ext JS is a JavaScript framework.
      #
      # In these sentences we don't want to link "Math" and "Ext" to the
      # corresponding JS classes.  And that's why we auto-link only
      # class names containing a dot "."
      #
      def create_magic_links(input)
        cls_re = "([A-Z][A-Za-z0-9.]*[A-Za-z0-9])"
        member_re = "(?:#([A-Za-z0-9]+))"

        input.gsub(/\b#{cls_re}#{member_re}?\b|#{member_re}\b/m) do
          replace_magic_link($1, $2 || $3)
        end
      end

      def replace_magic_link(cls, member)
        if cls && member
          if @relations[cls] && get_matching_member(cls, {:name => member})
            return link(cls, member, cls+"."+member)
          else
            warn_magic_link("#{cls}##{member} links to non-existing " + (@relations[cls] ? "member" : "class"))
          end
        elsif cls && cls =~ /\./
          if @relations[cls]
            return link(cls, nil, cls)
          else
            cls2, member2 = split_to_cls_and_member(cls)
            if @relations[cls2] && get_matching_member(cls2, {:name => member2})
              return link(cls2, member2, cls2+"."+member2)
            elsif cls =~ /\.(js|css|html|php)\Z/
              # Ignore common filenames
            else
              warn_magic_link("#{cls} links to non-existing class")
            end
          end
        elsif !cls && member
          if get_matching_member(@class_context, {:name => member})
            return link(@class_context, member, member)
          elsif member =~ /\A([A-F0-9]{3}|[A-F0-9]{6})\Z/i || member =~ /\A[0-9]/
            # Ignore HEX color codes and
            # member names beginning with number
          else
            warn_magic_link("##{member} links to non-existing member")
          end
        end

        return "#{cls}#{member ? '#' : ''}#{member}"
      end

      def split_to_cls_and_member(str)
        parts = str.split(/\./)
        return [parts.slice(0, parts.length-1).join("."), parts.last]
      end

      def warn_magic_link(msg)
        Logger.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
      end

      # applies the link template
      def link(cls, member, anchor_text, type=nil, static=nil)
        # Use the canonical class name for link (not some alternateClassName)
        cls = @relations[cls][:name]
        # prepend type name to member name
        member = member && get_matching_member(cls, {:name => member, :tagname => type, :static => static})

        @tpl.gsub(/(%[\w#-])/) do
          case $1
          when '%c'
            cls
          when '%m'
            member ? member[:id] : ""
          when '%#'
            member ? "#" : ""
          when '%-'
            member ? "-" : ""
          when '%a'
            Util::HTML.escape(anchor_text||"")
          else
            $1
          end
        end
      end

      def get_matching_member(cls, query)
        ms = find_members(cls, query)
        if ms.length > 1
          instance_ms = ms.find_all {|m| !m[:static] }
          instance_ms.length > 0 ? instance_ms[0] : ms.find_all {|m| m[:static] }[0]
        else
          ms[0]
        end
      end

      def find_members(cls, query)
        @relations[cls] ? @relations[cls].find_members(query) : []
      end

    end
Loading