1##
2# Subclass of the RDoc::Markup::ToHtml class that supports looking up method
3# names, classes, etc to create links.  RDoc::CrossReference is used to
4# generate those links based on the current context.
5
6class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
7
8  # :stopdoc:
9  ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP
10  CLASS_REGEXP_STR    = RDoc::CrossReference::CLASS_REGEXP_STR
11  CROSSREF_REGEXP     = RDoc::CrossReference::CROSSREF_REGEXP
12  METHOD_REGEXP_STR   = RDoc::CrossReference::METHOD_REGEXP_STR
13  # :startdoc:
14
15  ##
16  # RDoc::CodeObject for generating references
17
18  attr_accessor :context
19
20  ##
21  # Should we show '#' characters on method references?
22
23  attr_accessor :show_hash
24
25  ##
26  # Creates a new crossref resolver that generates links relative to +context+
27  # which lives at +from_path+ in the generated files.  '#' characters on
28  # references are removed unless +show_hash+ is true.  Only method names
29  # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
30
31  def initialize(options, from_path, context, markup = nil)
32    raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
33
34    super options, markup
35
36    @context       = context
37    @from_path     = from_path
38    @hyperlink_all = @options.hyperlink_all
39    @show_hash     = @options.show_hash
40
41    crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
42    @markup.add_special crossref_re, :CROSSREF
43
44    @cross_reference = RDoc::CrossReference.new @context
45  end
46
47  ##
48  # Creates a link to the reference +name+ if the name exists.  If +text+ is
49  # given it is used as the link text, otherwise +name+ is used.
50
51  def cross_reference name, text = nil
52    lookup = name
53
54    name = name[1..-1] unless @show_hash if name[0, 1] == '#'
55
56    name = "#{CGI.unescape $'} at #{$1}" if name =~ /(.*[^#:])@/
57
58    text = name unless text
59
60    link lookup, text
61  end
62
63  ##
64  # We're invoked when any text matches the CROSSREF pattern.  If we find the
65  # corresponding reference, generate a link.  If the name we're looking for
66  # contains no punctuation, we look for it up the module/class chain.  For
67  # example, ToHtml is found, even without the <tt>RDoc::Markup::</tt> prefix,
68  # because we look for it in module Markup first.
69
70  def handle_special_CROSSREF(special)
71    name = special.text
72
73    return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
74
75    unless @hyperlink_all then
76      # This ensures that words entirely consisting of lowercase letters will
77      # not have cross-references generated (to suppress lots of erroneous
78      # cross-references to "new" in text, for instance)
79      return name if name =~ /\A[a-z]*\z/
80    end
81
82    cross_reference name
83  end
84
85  ##
86  # Handles <tt>rdoc-ref:</tt> scheme links and allows RDoc::Markup::ToHtml to
87  # handle other schemes.
88
89  def handle_special_HYPERLINK special
90    return cross_reference $' if special.text =~ /\Ardoc-ref:/
91
92    super
93  end
94
95  ##
96  # +special+ is an rdoc-schemed link that will be converted into a hyperlink.
97  # For the rdoc-ref scheme the cross-reference will be looked up and the
98  # given name will be used.
99  #
100  # All other contents are handled by
101  # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_special_RDOCLINK]
102
103  def handle_special_RDOCLINK special
104    url = special.text
105
106    case url
107    when /\Ardoc-ref:/ then
108      cross_reference $'
109    else
110      super
111    end
112  end
113
114  ##
115  # Generates links for <tt>rdoc-ref:</tt> scheme URLs and allows
116  # RDoc::Markup::ToHtml to handle other schemes.
117
118  def gen_url url, text
119    return super unless url =~ /\Ardoc-ref:/
120
121    cross_reference $', text
122  end
123
124  ##
125  # Creates an HTML link to +name+ with the given +text+.
126
127  def link name, text
128    original_name = name
129
130    if name =~ /(.*[^#:])@/ then
131      name = $1
132      label = $'
133    end
134
135    ref = @cross_reference.resolve name, text
136
137    text = ref.output_name @context if
138      RDoc::MethodAttr === ref and text == original_name
139
140    case ref
141    when String then
142      ref
143    else
144      path = ref.as_href @from_path
145
146      if path =~ /#/ then
147        path << "-label-#{label}"
148      else
149        path << "#label-#{label}"
150      end if label
151
152      "<a href=\"#{path}\">#{text}</a>"
153    end
154  end
155
156end
157
158