PageRenderTime 61ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/benchmark/app/rdoc-2.4.3/lib/rdoc/markup/to_html.rb

http://github.com/rubinius/rubinius
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
  1. require 'rdoc/markup/formatter'
  2. require 'rdoc/markup/fragments'
  3. require 'rdoc/markup/inline'
  4. require 'cgi'
  5. class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
  6. LIST_TYPE_TO_HTML = {
  7. :BULLET => %w[<ul> </ul>],
  8. :NUMBER => %w[<ol> </ol>],
  9. :UPPERALPHA => %w[<ol> </ol>],
  10. :LOWERALPHA => %w[<ol> </ol>],
  11. :LABELED => %w[<dl> </dl>],
  12. :NOTE => %w[<table> </table>],
  13. }
  14. InlineTag = Struct.new(:bit, :on, :off)
  15. def initialize
  16. super
  17. # @in_tt - tt nested levels count
  18. # @tt_bit - cache
  19. @in_tt = 0
  20. @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
  21. # external hyperlinks
  22. @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
  23. # and links of the form <text>[<url>]
  24. @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
  25. init_tags
  26. end
  27. ##
  28. # Converts a target url to one that is relative to a given path
  29. def self.gen_relative_url(path, target)
  30. from = File.dirname path
  31. to, to_file = File.split target
  32. from = from.split "/"
  33. to = to.split "/"
  34. from.delete '.'
  35. to.delete '.'
  36. while from.size > 0 and to.size > 0 and from[0] == to[0] do
  37. from.shift
  38. to.shift
  39. end
  40. from.fill ".."
  41. from.concat to
  42. from << to_file
  43. File.join(*from)
  44. end
  45. ##
  46. # Generate a hyperlink for url, labeled with text. Handle the
  47. # special cases for img: and link: described under handle_special_HYPERLINK
  48. def gen_url(url, text)
  49. if url =~ /([A-Za-z]+):(.*)/ then
  50. type = $1
  51. path = $2
  52. else
  53. type = "http"
  54. path = url
  55. url = "http://#{url}"
  56. end
  57. if type == "link" then
  58. url = if path[0, 1] == '#' then # is this meaningful?
  59. path
  60. else
  61. self.class.gen_relative_url @from_path, path
  62. end
  63. end
  64. if (type == "http" or type == "link") and
  65. url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
  66. "<img src=\"#{url}\" />"
  67. else
  68. "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
  69. end
  70. end
  71. ##
  72. # And we're invoked with a potential external hyperlink mailto:
  73. # just gets inserted. http: links are checked to see if they
  74. # reference an image. If so, that image gets inserted using an
  75. # <img> tag. Otherwise a conventional <a href> is used. We also
  76. # support a special type of hyperlink, link:, which is a reference
  77. # to a local file whose path is relative to the --op directory.
  78. def handle_special_HYPERLINK(special)
  79. url = special.text
  80. gen_url url, url
  81. end
  82. ##
  83. # Here's a hypedlink where the label is different to the URL
  84. # <label>[url] or {long label}[url]
  85. def handle_special_TIDYLINK(special)
  86. text = special.text
  87. return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
  88. label = $1
  89. url = $2
  90. gen_url url, label
  91. end
  92. ##
  93. # are we currently inside tt tags?
  94. def in_tt?
  95. @in_tt > 0
  96. end
  97. ##
  98. # is +tag+ a tt tag?
  99. def tt?(tag)
  100. tag.bit == @tt_bit
  101. end
  102. ##
  103. # Set up the standard mapping of attributes to HTML tags
  104. def init_tags
  105. @attr_tags = [
  106. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
  107. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
  108. InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
  109. ]
  110. end
  111. ##
  112. # Add a new set of HTML tags for an attribute. We allow separate start and
  113. # end tags for flexibility.
  114. def add_tag(name, start, stop)
  115. @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
  116. end
  117. ##
  118. # Given an HTML tag, decorate it with class information and the like if
  119. # required. This is a no-op in the base class, but is overridden in HTML
  120. # output classes that implement style sheets.
  121. def annotate(tag)
  122. tag
  123. end
  124. ##
  125. # This is a higher speed (if messier) version of wrap
  126. def wrap(txt, line_len = 76)
  127. res = ""
  128. sp = 0
  129. ep = txt.length
  130. while sp < ep
  131. # scan back for a space
  132. p = sp + line_len - 1
  133. if p >= ep
  134. p = ep
  135. else
  136. while p > sp and txt[p] != ?\s
  137. p -= 1
  138. end
  139. if p <= sp
  140. p = sp + line_len
  141. while p < ep and txt[p] != ?\s
  142. p += 1
  143. end
  144. end
  145. end
  146. res << txt[sp...p] << "\n"
  147. sp = p
  148. sp += 1 while sp < ep and txt[sp] == ?\s
  149. end
  150. res
  151. end
  152. ##
  153. # :section: Visitor
  154. def start_accepting
  155. @res = ""
  156. @in_list_entry = []
  157. end
  158. def end_accepting
  159. @res
  160. end
  161. def accept_paragraph(am, fragment)
  162. @res << annotate("<p>") + "\n"
  163. @res << wrap(convert_flow(am.flow(fragment.txt)))
  164. @res << annotate("</p>") + "\n"
  165. end
  166. def accept_verbatim(am, fragment)
  167. @res << annotate("<pre>") + "\n"
  168. @res << CGI.escapeHTML(fragment.txt)
  169. @res << annotate("</pre>") << "\n"
  170. end
  171. def accept_rule(am, fragment)
  172. size = fragment.param
  173. size = 10 if size > 10
  174. @res << "<hr size=\"#{size}\"></hr>"
  175. end
  176. def accept_list_start(am, fragment)
  177. @res << html_list_name(fragment.type, true) << "\n"
  178. @in_list_entry.push false
  179. end
  180. def accept_list_end(am, fragment)
  181. if tag = @in_list_entry.pop
  182. @res << annotate(tag) << "\n"
  183. end
  184. @res << html_list_name(fragment.type, false) << "\n"
  185. end
  186. def accept_list_item(am, fragment)
  187. if tag = @in_list_entry.last
  188. @res << annotate(tag) << "\n"
  189. end
  190. @res << list_item_start(am, fragment)
  191. @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
  192. @in_list_entry[-1] = list_end_for(fragment.type)
  193. end
  194. def accept_blank_line(am, fragment)
  195. # @res << annotate("<p />") << "\n"
  196. end
  197. def accept_heading(am, fragment)
  198. @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
  199. end
  200. private
  201. def on_tags(res, item)
  202. attr_mask = item.turn_on
  203. return if attr_mask.zero?
  204. @attr_tags.each do |tag|
  205. if attr_mask & tag.bit != 0
  206. res << annotate(tag.on)
  207. @in_tt += 1 if tt?(tag)
  208. end
  209. end
  210. end
  211. def off_tags(res, item)
  212. attr_mask = item.turn_off
  213. return if attr_mask.zero?
  214. @attr_tags.reverse_each do |tag|
  215. if attr_mask & tag.bit != 0
  216. @in_tt -= 1 if tt?(tag)
  217. res << annotate(tag.off)
  218. end
  219. end
  220. end
  221. def convert_flow(flow)
  222. res = ""
  223. flow.each do |item|
  224. case item
  225. when String
  226. res << convert_string(item)
  227. when RDoc::Markup::AttrChanger
  228. off_tags(res, item)
  229. on_tags(res, item)
  230. when RDoc::Markup::Special
  231. res << convert_special(item)
  232. else
  233. raise "Unknown flow element: #{item.inspect}"
  234. end
  235. end
  236. res
  237. end
  238. def convert_string(item)
  239. in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
  240. end
  241. def convert_string_simple(item)
  242. CGI.escapeHTML item
  243. end
  244. ##
  245. # some of these patterns are taken from SmartyPants...
  246. def convert_string_fancy(item)
  247. # convert ampersand before doing anything else
  248. item.gsub(/&/, '&amp;').
  249. # convert -- to em-dash, (-- to en-dash)
  250. gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
  251. # convert ... to elipsis (and make sure .... becomes .<elipsis>)
  252. gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
  253. # convert single closing quote
  254. gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;'). # }
  255. gsub(%r{\'(?=\W|s\b)}, '&#8217;').
  256. # convert single opening quote
  257. gsub(/'/, '&#8216;').
  258. # convert double closing quote
  259. gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1&#8221;'). # }
  260. # convert double opening quote
  261. gsub(/"/, '&#8220;').
  262. # convert copyright
  263. gsub(/\(c\)/, '&#169;').
  264. # convert registered trademark
  265. gsub(/\(r\)/, '&#174;')
  266. end
  267. def convert_special(special)
  268. handled = false
  269. RDoc::Markup::Attribute.each_name_of(special.type) do |name|
  270. method_name = "handle_special_#{name}"
  271. if self.respond_to? method_name
  272. special.text = send(method_name, special)
  273. handled = true
  274. end
  275. end
  276. raise "Unhandled special: #{special}" unless handled
  277. special.text
  278. end
  279. def convert_heading(level, flow)
  280. res =
  281. annotate("<h#{level}>") +
  282. convert_flow(flow) +
  283. annotate("</h#{level}>\n")
  284. end
  285. def html_list_name(list_type, is_open_tag)
  286. tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
  287. annotate(tags[ is_open_tag ? 0 : 1])
  288. end
  289. def list_item_start(am, fragment)
  290. case fragment.type
  291. when :BULLET, :NUMBER then
  292. annotate("<li>")
  293. when :UPPERALPHA then
  294. annotate("<li type=\"A\">")
  295. when :LOWERALPHA then
  296. annotate("<li type=\"a\">")
  297. when :LABELED then
  298. annotate("<dt>") +
  299. convert_flow(am.flow(fragment.param)) +
  300. annotate("</dt>") +
  301. annotate("<dd>")
  302. when :NOTE then
  303. annotate("<tr>") +
  304. annotate("<td valign=\"top\">") +
  305. convert_flow(am.flow(fragment.param)) +
  306. annotate("</td>") +
  307. annotate("<td>")
  308. else
  309. raise "Invalid list type"
  310. end
  311. end
  312. def list_end_for(fragment_type)
  313. case fragment_type
  314. when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
  315. "</li>"
  316. when :LABELED then
  317. "</dd>"
  318. when :NOTE then
  319. "</td></tr>"
  320. else
  321. raise "Invalid list type"
  322. end
  323. end
  324. end