/benchmark/app/rdoc-2.4.3/lib/rdoc/markup/to_html.rb
Ruby | 406 lines | 292 code | 68 blank | 46 comment | 31 complexity | 6167b4529769e4cc97c746355706ebdf MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception, 0BSD, GPL-2.0, LGPL-2.1
- require 'rdoc/markup/formatter'
- require 'rdoc/markup/fragments'
- require 'rdoc/markup/inline'
- require 'cgi'
- class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
- LIST_TYPE_TO_HTML = {
- :BULLET => %w[<ul> </ul>],
- :NUMBER => %w[<ol> </ol>],
- :UPPERALPHA => %w[<ol> </ol>],
- :LOWERALPHA => %w[<ol> </ol>],
- :LABELED => %w[<dl> </dl>],
- :NOTE => %w[<table> </table>],
- }
- InlineTag = Struct.new(:bit, :on, :off)
- def initialize
- super
- # @in_tt - tt nested levels count
- # @tt_bit - cache
- @in_tt = 0
- @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
- # external hyperlinks
- @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
- # and links of the form <text>[<url>]
- @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
- init_tags
- end
- ##
- # Converts a target url to one that is relative to a given path
- def self.gen_relative_url(path, target)
- from = File.dirname path
- to, to_file = File.split target
- from = from.split "/"
- to = to.split "/"
- from.delete '.'
- to.delete '.'
- while from.size > 0 and to.size > 0 and from[0] == to[0] do
- from.shift
- to.shift
- end
- from.fill ".."
- from.concat to
- from << to_file
- File.join(*from)
- end
- ##
- # Generate a hyperlink for url, labeled with text. Handle the
- # special cases for img: and link: described under handle_special_HYPERLINK
- def gen_url(url, text)
- if url =~ /([A-Za-z]+):(.*)/ then
- type = $1
- path = $2
- else
- type = "http"
- path = url
- url = "http://#{url}"
- end
- if type == "link" then
- url = if path[0, 1] == '#' then # is this meaningful?
- path
- else
- self.class.gen_relative_url @from_path, path
- end
- end
- if (type == "http" or type == "link") and
- url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
- "<img src=\"#{url}\" />"
- else
- "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
- end
- end
- ##
- # And we're invoked with a potential external hyperlink mailto:
- # just gets inserted. http: links are checked to see if they
- # reference an image. If so, that image gets inserted using an
- # <img> tag. Otherwise a conventional <a href> is used. We also
- # support a special type of hyperlink, link:, which is a reference
- # to a local file whose path is relative to the --op directory.
- def handle_special_HYPERLINK(special)
- url = special.text
- gen_url url, url
- end
- ##
- # Here's a hypedlink where the label is different to the URL
- # <label>[url] or {long label}[url]
- def handle_special_TIDYLINK(special)
- text = special.text
- return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
- label = $1
- url = $2
- gen_url url, label
- end
- ##
- # are we currently inside tt tags?
- def in_tt?
- @in_tt > 0
- end
- ##
- # is +tag+ a tt tag?
- def tt?(tag)
- tag.bit == @tt_bit
- end
- ##
- # Set up the standard mapping of attributes to HTML tags
- def init_tags
- @attr_tags = [
- InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
- InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
- InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
- ]
- end
- ##
- # Add a new set of HTML tags for an attribute. We allow separate start and
- # end tags for flexibility.
- def add_tag(name, start, stop)
- @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
- end
- ##
- # Given an HTML tag, decorate it with class information and the like if
- # required. This is a no-op in the base class, but is overridden in HTML
- # output classes that implement style sheets.
- def annotate(tag)
- tag
- end
- ##
- # This is a higher speed (if messier) version of wrap
- def wrap(txt, line_len = 76)
- res = ""
- sp = 0
- ep = txt.length
- while sp < ep
- # scan back for a space
- p = sp + line_len - 1
- if p >= ep
- p = ep
- else
- while p > sp and txt[p] != ?\s
- p -= 1
- end
- if p <= sp
- p = sp + line_len
- while p < ep and txt[p] != ?\s
- p += 1
- end
- end
- end
- res << txt[sp...p] << "\n"
- sp = p
- sp += 1 while sp < ep and txt[sp] == ?\s
- end
- res
- end
- ##
- # :section: Visitor
- def start_accepting
- @res = ""
- @in_list_entry = []
- end
- def end_accepting
- @res
- end
- def accept_paragraph(am, fragment)
- @res << annotate("<p>") + "\n"
- @res << wrap(convert_flow(am.flow(fragment.txt)))
- @res << annotate("</p>") + "\n"
- end
- def accept_verbatim(am, fragment)
- @res << annotate("<pre>") + "\n"
- @res << CGI.escapeHTML(fragment.txt)
- @res << annotate("</pre>") << "\n"
- end
- def accept_rule(am, fragment)
- size = fragment.param
- size = 10 if size > 10
- @res << "<hr size=\"#{size}\"></hr>"
- end
- def accept_list_start(am, fragment)
- @res << html_list_name(fragment.type, true) << "\n"
- @in_list_entry.push false
- end
- def accept_list_end(am, fragment)
- if tag = @in_list_entry.pop
- @res << annotate(tag) << "\n"
- end
- @res << html_list_name(fragment.type, false) << "\n"
- end
- def accept_list_item(am, fragment)
- if tag = @in_list_entry.last
- @res << annotate(tag) << "\n"
- end
- @res << list_item_start(am, fragment)
- @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
- @in_list_entry[-1] = list_end_for(fragment.type)
- end
- def accept_blank_line(am, fragment)
- # @res << annotate("<p />") << "\n"
- end
- def accept_heading(am, fragment)
- @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
- end
- private
- def on_tags(res, item)
- attr_mask = item.turn_on
- return if attr_mask.zero?
- @attr_tags.each do |tag|
- if attr_mask & tag.bit != 0
- res << annotate(tag.on)
- @in_tt += 1 if tt?(tag)
- end
- end
- end
- def off_tags(res, item)
- attr_mask = item.turn_off
- return if attr_mask.zero?
- @attr_tags.reverse_each do |tag|
- if attr_mask & tag.bit != 0
- @in_tt -= 1 if tt?(tag)
- res << annotate(tag.off)
- end
- end
- end
- def convert_flow(flow)
- res = ""
- flow.each do |item|
- case item
- when String
- res << convert_string(item)
- when RDoc::Markup::AttrChanger
- off_tags(res, item)
- on_tags(res, item)
- when RDoc::Markup::Special
- res << convert_special(item)
- else
- raise "Unknown flow element: #{item.inspect}"
- end
- end
- res
- end
- def convert_string(item)
- in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
- end
- def convert_string_simple(item)
- CGI.escapeHTML item
- end
- ##
- # some of these patterns are taken from SmartyPants...
- def convert_string_fancy(item)
- # convert ampersand before doing anything else
- item.gsub(/&/, '&').
- # convert -- to em-dash, (-- to en-dash)
- gsub(/---?/, '—'). #gsub(/--/, '–').
- # convert ... to elipsis (and make sure .... becomes .<elipsis>)
- gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…').
- # convert single closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1’'). # }
- gsub(%r{\'(?=\W|s\b)}, '’').
- # convert single opening quote
- gsub(/'/, '‘').
- # convert double closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1”'). # }
- # convert double opening quote
- gsub(/"/, '“').
- # convert copyright
- gsub(/\(c\)/, '©').
- # convert registered trademark
- gsub(/\(r\)/, '®')
- end
- def convert_special(special)
- handled = false
- RDoc::Markup::Attribute.each_name_of(special.type) do |name|
- method_name = "handle_special_#{name}"
- if self.respond_to? method_name
- special.text = send(method_name, special)
- handled = true
- end
- end
- raise "Unhandled special: #{special}" unless handled
- special.text
- end
- def convert_heading(level, flow)
- res =
- annotate("<h#{level}>") +
- convert_flow(flow) +
- annotate("</h#{level}>\n")
- end
- def html_list_name(list_type, is_open_tag)
- tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
- annotate(tags[ is_open_tag ? 0 : 1])
- end
- def list_item_start(am, fragment)
- case fragment.type
- when :BULLET, :NUMBER then
- annotate("<li>")
- when :UPPERALPHA then
- annotate("<li type=\"A\">")
- when :LOWERALPHA then
- annotate("<li type=\"a\">")
- when :LABELED then
- annotate("<dt>") +
- convert_flow(am.flow(fragment.param)) +
- annotate("</dt>") +
- annotate("<dd>")
- when :NOTE then
- annotate("<tr>") +
- annotate("<td valign=\"top\">") +
- convert_flow(am.flow(fragment.param)) +
- annotate("</td>") +
- annotate("<td>")
- else
- raise "Invalid list type"
- end
- end
- def list_end_for(fragment_type)
- case fragment_type
- when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
- "</li>"
- when :LABELED then
- "</dd>"
- when :NOTE then
- "</td></tr>"
- else
- raise "Invalid list type"
- end
- end
- end