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

Better automatic linking of class names.

Previous implementation was very hackish. Now only doing auto-detection
when not inside HTML tag (i.e. between <...>) and when not already
inside HTML link (i.e. between <a>...</a>).

As a result the #replace_class_names method is quite a bit simpler, as
it can be sure that it's always executed in valid context.
parent 0f2a42ef
Loading
Loading
Loading
Loading
+30 −11
Original line number Diff line number Diff line
@@ -84,21 +84,42 @@ module JsDuck
    def replace(input)
      s = StringScanner.new(input)
      out = ""

      # Keep track of the nesting level of <a> tags. We're not
      # auto-detecting class names when inside <a>. Normally links
      # shouldn't be nested, but just to be extra safe.
      open_a_tags = 0

      while !s.eos? do
        if s.check(@link_re)
          out += replace_link_tag(s.scan(@link_re))
        elsif s.check(@img_re)
          out += replace_img_tag(s.scan(@img_re))
        elsif s.check(/[{]/)
          # There might still be "{" that doesn't begin {@link} or {@img} - ignore it
          out += s.scan(/[{]/)
        elsif s.check(@example_annotation_re)
          # Match possible classnames following @example and add them
          # as CSS classes inside <pre> element.
          s.scan(@example_annotation_re) =~ @example_annotation_re
          css_classes = ($1 || "").strip
          out += "<pre class='inline-example #{css_classes}'><code>"
        elsif s.check(/[{<]/)
          out += s.scan(/[{<]/)
        elsif s.check(/<a\b/)
          # Increment number of open <a> tags.
          open_a_tags += 1
          out += s.scan_until(/>|\Z/)
        elsif s.check(/<\/a>/)
          # <a> closed, auto-detection may continue when no more <a> tags open.
          open_a_tags -= 1
          out += s.scan(/<\/a>/)
        elsif s.check(/<.*?>/)
          # Ignore all other HTML tags
          out += s.scan_until(/>|\Z/)
        else
          out += replace_class_names(s.scan(/[^{<]+/))
          # Replace class names in the following text up to next "<" or "{"
          # but only when we're not inside <a>...</a>
          text = s.scan(/[^{<]+/)
          out += open_a_tags > 0 ? text : replace_class_names(text)
        end
      end
      out
@@ -175,18 +196,16 @@ module JsDuck
    end

    def replace_class_names(input)
      input.gsub(/(\A|\s)([A-Z][A-Za-z0-9.]*[A-Za-z0-9])(?:(#)([A-Za-z0-9]+))?([.,]?(?:\s|\Z))/m) do
        before = $1
        cls = $2
        hash = $3
        member = $4
        after = $5
      input.gsub(/\b([A-Z][A-Za-z0-9.]*[A-Za-z0-9])(?:(#)([A-Za-z0-9]+))?\b/m) do
        cls = $1
        hash = $2
        member = $3

        if @relations[cls] && (member ? get_matching_member(cls, member) : cls =~ /\./)
          label = member ? cls+"."+member : cls
          before + link(cls, member, label) + after
          link(cls, member, label)
        else
          before + cls + (hash || "") + (member || "") + after
          cls + (hash || "") + (member || "")
        end
      end
    end
+43 −0
Original line number Diff line number Diff line
@@ -188,6 +188,11 @@ describe JsDuck::DocFormatter do
          'Look at <a href="Foo.Bar">Foo.Bar</a>, it\'s great!'
      end

      it "converts two ClassNames in one line to links" do
        @formatter.replace("See: Foo.Bar, Ext.XTemplate").should ==
          'See: <a href="Foo.Bar">Foo.Bar</a>, <a href="Ext.XTemplate">Ext.XTemplate</a>'
      end

      it "converts Ext#encode to method link" do
        @formatter.replace("Look at Ext#encode").should ==
          'Look at <a href="Ext#method-encode">Ext.encode</a>'
@@ -198,6 +203,8 @@ describe JsDuck::DocFormatter do
          'Look at <a href="Ext.form.Field#method-getValues">Ext.form.Field.getValues</a>'
      end

      # Ensure links aren't created inside <a>...</a> or {@link} and {@img} tags.

      it "doesn't create links inside {@link} tag" do
        @formatter.replace("{@link Foo.Bar a Foo.Bar link}").should ==
          '<a href="Foo.Bar">a Foo.Bar link</a>'
@@ -207,6 +214,42 @@ describe JsDuck::DocFormatter do
        @formatter.replace("{@img some/file.jpg a Foo.Bar image}").should ==
          '<img src="some/file.jpg" alt="a Foo.Bar image"/>'
      end

      it "doesn't create links inside HTML tags" do
        @formatter.replace('<img src="pics/Foo.Bar"/>').should ==
          '<img src="pics/Foo.Bar"/>'
      end

      it "doesn't create links inside multiline HTML tags" do
        @formatter.replace('<img\nsrc="pics/Foo.Bar"/>').should ==
          '<img\nsrc="pics/Foo.Bar"/>'
      end

      it "doesn't create links inside <a>...</a>" do
        @formatter.replace('See <a href="Foo.Bar">Foo.Bar</a>').should ==
          'See <a href="Foo.Bar">Foo.Bar</a>'
      end

      it "creates links inside <b>...</b>" do
        @formatter.replace('See <b>Foo.Bar</b>').should ==
          'See <b><a href="Foo.Bar">Foo.Bar</a></b>'
      end

      it "doesn't create links inside <a><b>...</b></a>" do
        @formatter.replace('See <a href="Foo.Bar"><b>Foo.Bar</b></a>').should ==
          'See <a href="Foo.Bar"><b>Foo.Bar</b></a>'
      end

      it "creates links after <a>...</a>" do
        @formatter.replace('See <a href="Foo.Bar">Foo.Bar</a> and Ext.XTemplate.').should ==
          'See <a href="Foo.Bar">Foo.Bar</a> and <a href="Ext.XTemplate">Ext.XTemplate</a>.'
      end

      it "doesn't create links inside nested <a> tags" do
        @formatter.replace('See <a href="Foo.Bar"><a>Foo.Bar</a> Ext.XTemplate</a>').should ==
          'See <a href="Foo.Bar"><a>Foo.Bar</a> Ext.XTemplate</a>'
      end

    end

    describe "with type information" do