Commit aed18021 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Template-based replacement of {@link} and {@img}.

Adjusting the markup of links and images is now much more flexible.
This will be most useful for customizing the markup in exports.

The tests also got simpler, as we can test agains simpler markup now.
parent aad116ec
Loading
Loading
Loading
Loading
+54 −25
Original line number Diff line number Diff line
require 'rubygems'
require 'rdiscount'
require 'strscan'
require 'cgi'

module JsDuck

  # Formats doc-comments
  class DocFormatter
    # CSS class to add to each link
    attr_accessor :css_class
    # Template HTML that replaces {@link Class#member anchor text}.
    # Can contain placeholders:
    #
    # %c - full class name (e.g. "Ext.Panel")
    # %m - class member name (e.g. "urlEncode")
    # %M - class member name, prefixed with hash (e.g. "#urlEncode")
    # %a - anchor text for link
    #
    # Default value: '<a href="%c%M">%a</a>'
    attr_accessor :link_tpl

    # Template for the href URL.
    # Can contain %cls% which is replaced with actual classname.
    # Also '#' and member name is appended to link if needed
    attr_accessor :url_template
    # Template HTML that replaces {@img URL alt text}
    # Can contain placeholders:
    #
    # %u - URL from @img tag (e.g. "some/path.png")
    # %a - alt text for image
    #
    # Default value: '<img src="%u" alt="%a"/>'
    attr_accessor :img_tpl

    # Sets up instance to work in context of particular class, so
    # that when {@link #blah} is encountered it knows that
@@ -31,10 +44,10 @@ module JsDuck

    def initialize
      @context = ""
      @css_class = nil
      @url_template = "%cls%"
      @max_length = 120
      @relations = {}
      @link_tpl = '<a href="%c%M">%a</a>'
      @img_tpl = '<img src="%u" alt="%a"/>'
      @link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
      @img_re = /\{@img\s+(\S*?)(?:\s+(.+?))?\}/m
    end
@@ -43,11 +56,9 @@ module JsDuck
    # recognized classnames.
    #
    # Replaces {@link Class#member link text} in given string with
    # HTML links pointing to documentation.  In addition to the href
    # attribute links will also contain ext:cls and ext:member
    # attributes.
    # HTML from @link_tpl.
    #
    # Replaces {@img path/to/image.jpg Alt text} with HTML <img> tag.
    # Replaces {@img path/to/image.jpg Alt text} with HTML from @img_tpl.
    #
    # Additionally replaces strings recognized as ClassNames with
    # links to these classes.  So one doesn even need to use the @link
@@ -95,11 +106,7 @@ module JsDuck
    end

    def replace_img_tag(input)
      input.sub(@img_re) do
        src = $1
        alt = $2
        "<img src=\"#{src}\" alt=\"#{alt}\"/>"
      end
      input.sub(@img_re) { img($1, $2) }
    end

    def replace_class_names(input)
@@ -119,14 +126,36 @@ module JsDuck
      end
    end

    # Creates HTML link to class and/or member
    def link(cls, member, label)
      anchor = member ? "#" + member : ""
      url = @url_template.sub(/%cls%/, cls) + anchor
      href = ' href="' + url + '"'
      rel = ' rel="' + cls + anchor + '"'
      cssCls = @css_class ? ' class="' + @css_class + '"' : ''
      "<a" + href + rel + cssCls + ">" + label + "</a>"
    # applies the image template
    def img(url, alt_text)
      @img_tpl.gsub(/(%\w)/) do
        case $1
        when '%u'
          url
        when '%a'
          CGI.escapeHTML(alt_text||"")
        else
          $1
        end
      end
    end

    # applies the link template
    def link(cls, member, anchor_text)
      @link_tpl.gsub(/(%\w)/) do
        case $1
        when '%c'
          cls
        when '%m'
          member ? member : ""
        when '%M'
          member ? "#"+member : ""
        when '%a'
          CGI.escapeHTML(anchor_text||"")
        else
          $1
        end
      end
    end

    # Formats doc-comment for placement into HTML.
+1 −2
Original line number Diff line number Diff line
@@ -22,8 +22,7 @@ module JsDuck
      @cache = cache
      @formatter = DocFormatter.new
      @formatter.context = cls.full_name
      @formatter.css_class = 'docClass'
      @formatter.url_template = 'output/%cls%.html'
      @formatter.link_tpl = '<a href="output/%c.html%M" rel="%c%M" class="docClass">%a</a>'
      @formatter.relations = relations
    end

+25 −15
Original line number Diff line number Diff line
@@ -13,22 +13,22 @@ describe JsDuck::DocFormatter do

    it "replaces {@link Ext.Msg} with link to class" do
      @formatter.replace("Look at {@link Ext.Msg}").should ==
        'Look at <a href="Ext.Msg" rel="Ext.Msg">Ext.Msg</a>'
        'Look at <a href="Ext.Msg">Ext.Msg</a>'
    end

    it "replaces {@link Foo#bar} with link to class member" do
      @formatter.replace("Look at {@link Foo#bar}").should ==
        'Look at <a href="Foo#bar" rel="Foo#bar">Foo.bar</a>'
        'Look at <a href="Foo#bar">Foo.bar</a>'
    end

    it "uses context to replace {@link #bar} with link to class member" do
      @formatter.replace("Look at {@link #bar}").should ==
        'Look at <a href="Context#bar" rel="Context#bar">bar</a>'
        'Look at <a href="Context#bar">bar</a>'
    end

    it "allows use of custom link text" do
      @formatter.replace("Look at {@link Foo link text}").should ==
        'Look at <a href="Foo" rel="Foo">link text</a>'
        'Look at <a href="Foo">link text</a>'
    end

    it "leaves text without {@link...} untouched" do
@@ -43,12 +43,17 @@ describe JsDuck::DocFormatter do

    it "handles {@link} spanning multiple lines" do
      @formatter.replace("Look at {@link\nExt.Msg\nsome text}").should ==
        'Look at <a href="Ext.Msg" rel="Ext.Msg">some text</a>'
        'Look at <a href="Ext.Msg">some text</a>'
    end

    it "handles {@link} with label spanning multiple lines" do
      @formatter.replace("Look at {@link Ext.Msg some\ntext}").should ==
        "Look at <a href=\"Ext.Msg\" rel=\"Ext.Msg\">some\ntext</a>"
        "Look at <a href=\"Ext.Msg\">some\ntext</a>"
    end

    it "escapes link text" do
      @formatter.replace('{@link Ext.Msg <bla>}').should ==
        '<a href="Ext.Msg">&lt;bla&gt;</a>'
    end

    # {@img ...}
@@ -63,6 +68,11 @@ describe JsDuck::DocFormatter do
        'Look at <img src="some/image.png" alt=""/>'
    end

    it "escapes image alt text" do
      @formatter.replace('{@img some/image.png foo"bar}').should ==
        '<img src="some/image.png" alt="foo&quot;bar"/>'
    end

    # auto-conversion of identifiable ClassNames to links
    describe "auto-detect" do
      before do
@@ -88,47 +98,47 @@ describe JsDuck::DocFormatter do

      it "converts FooBar to class link" do
        @formatter.replace("Look at FooBar").should ==
          "Look at <a href=\"FooBar\" rel=\"FooBar\">FooBar</a>"
          'Look at <a href="FooBar">FooBar</a>'
      end

      it "converts FooBar.Blah to class link" do
        @formatter.replace("Look at FooBar.Blah").should ==
          "Look at <a href=\"FooBar.Blah\" rel=\"FooBar.Blah\">FooBar.Blah</a>"
          'Look at <a href="FooBar.Blah">FooBar.Blah</a>'
      end

      it "converts Ext.form.Field to class link" do
        @formatter.replace("Look at Ext.form.Field").should ==
          "Look at <a href=\"Ext.form.Field\" rel=\"Ext.form.Field\">Ext.form.Field</a>"
          'Look at <a href="Ext.form.Field">Ext.form.Field</a>'
      end

      it "converts Ext.XTemplate to class link" do
        @formatter.replace("Look at Ext.XTemplate").should ==
          "Look at <a href=\"Ext.XTemplate\" rel=\"Ext.XTemplate\">Ext.XTemplate</a>"
          'Look at <a href="Ext.XTemplate">Ext.XTemplate</a>'
      end

      it "converts ClassName ending with dot to class link" do
        @formatter.replace("Look at MyClass.").should ==
          "Look at <a href=\"MyClass\" rel=\"MyClass\">MyClass</a>."
          'Look at <a href="MyClass">MyClass</a>.'
      end

      it "converts ClassName ending with comma to class link" do
        @formatter.replace("Look at MyClass, it's great!").should ==
          "Look at <a href=\"MyClass\" rel=\"MyClass\">MyClass</a>, it's great!"
          'Look at <a href="MyClass">MyClass</a>, it\'s great!'
      end

      it "converts Ext#encode to method link" do
        @formatter.replace("Look at Ext#encode").should ==
          "Look at <a href=\"Ext#encode\" rel=\"Ext#encode\">Ext.encode</a>"
          'Look at <a href="Ext#encode">Ext.encode</a>'
      end

      it "converts Ext.form.Field#getValues to method link" do
        @formatter.replace("Look at Ext.form.Field#getValues").should ==
          "Look at <a href=\"Ext.form.Field#getValues\" rel=\"Ext.form.Field#getValues\">Ext.form.Field.getValues</a>"
          'Look at <a href="Ext.form.Field#getValues">Ext.form.Field.getValues</a>'
      end

      it "doesn't create links inside {@link} tag" do
        @formatter.replace("{@link MyClass a MyClass link}").should ==
          '<a href="MyClass" rel="MyClass">a MyClass link</a>'
          '<a href="MyClass">a MyClass link</a>'
      end

      it "doesn't create links inside {@img} tag" do