PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/preinstalled-gems/data/gems/rdoc-2.5.1/lib/rdoc/markup/to_html.rb

http://github.com/rubinius/rubinius
Ruby | 340 lines | 240 code | 58 blank | 42 comment | 22 complexity | 936bd3424a878af3fec60605d3f7736e 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/inline'
  3. require 'cgi'
  4. ##
  5. # Outputs RDoc markup as HTML
  6. class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
  7. ##
  8. # Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
  9. LIST_TYPE_TO_HTML = {
  10. :BULLET => ['<ul>', '</ul>'],
  11. :LABEL => ['<dl>', '</dl>'],
  12. :LALPHA => ['<ol style="display: lower-alpha">', '</ol>'],
  13. :NOTE => ['<table>', '</table>'],
  14. :NUMBER => ['<ol>', '</ol>'],
  15. :UALPHA => ['<ol style="display: upper-alpha">', '</ol>'],
  16. }
  17. attr_reader :res # :nodoc:
  18. attr_reader :in_list_entry # :nodoc:
  19. attr_reader :list # :nodoc:
  20. ##
  21. # Converts a target url to one that is relative to a given path
  22. def self.gen_relative_url(path, target)
  23. from = File.dirname path
  24. to, to_file = File.split target
  25. from = from.split "/"
  26. to = to.split "/"
  27. from.delete '.'
  28. to.delete '.'
  29. while from.size > 0 and to.size > 0 and from[0] == to[0] do
  30. from.shift
  31. to.shift
  32. end
  33. from.fill ".."
  34. from.concat to
  35. from << to_file
  36. File.join(*from)
  37. end
  38. def initialize
  39. super
  40. @th = nil
  41. @in_list_entry = nil
  42. @list = nil
  43. # external hyperlinks
  44. @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
  45. # and links of the form <text>[<url>]
  46. @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
  47. init_tags
  48. end
  49. ##
  50. # Maps attributes to HTML tags
  51. def init_tags
  52. add_tag :BOLD, "<b>", "</b>"
  53. add_tag :TT, "<tt>", "</tt>"
  54. add_tag :EM, "<em>", "</em>"
  55. end
  56. ##
  57. # Generate a hyperlink for url, labeled with text. Handle the
  58. # special cases for img: and link: described under handle_special_HYPERLINK
  59. def gen_url(url, text)
  60. if url =~ /([A-Za-z]+):(.*)/ then
  61. type = $1
  62. path = $2
  63. else
  64. type = "http"
  65. path = url
  66. url = "http://#{url}"
  67. end
  68. if type == "link" then
  69. url = if path[0, 1] == '#' then # is this meaningful?
  70. path
  71. else
  72. self.class.gen_relative_url @from_path, path
  73. end
  74. end
  75. if (type == "http" or type == "link") and
  76. url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
  77. "<img src=\"#{url}\" />"
  78. else
  79. "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
  80. end
  81. end
  82. ##
  83. # And we're invoked with a potential external hyperlink mailto:
  84. # just gets inserted. http: links are checked to see if they
  85. # reference an image. If so, that image gets inserted using an
  86. # <img> tag. Otherwise a conventional <a href> is used. We also
  87. # support a special type of hyperlink, link:, which is a reference
  88. # to a local file whose path is relative to the --op directory.
  89. def handle_special_HYPERLINK(special)
  90. url = special.text
  91. gen_url url, url
  92. end
  93. ##
  94. # Here's a hypedlink where the label is different to the URL
  95. # <label>[url] or {long label}[url]
  96. def handle_special_TIDYLINK(special)
  97. text = special.text
  98. return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
  99. label = $1
  100. url = $2
  101. gen_url url, label
  102. end
  103. ##
  104. # This is a higher speed (if messier) version of wrap
  105. def wrap(txt, line_len = 76)
  106. res = []
  107. sp = 0
  108. ep = txt.length
  109. while sp < ep
  110. # scan back for a space
  111. p = sp + line_len - 1
  112. if p >= ep
  113. p = ep
  114. else
  115. while p > sp and txt[p] != ?\s
  116. p -= 1
  117. end
  118. if p <= sp
  119. p = sp + line_len
  120. while p < ep and txt[p] != ?\s
  121. p += 1
  122. end
  123. end
  124. end
  125. res << txt[sp...p] << "\n"
  126. sp = p
  127. sp += 1 while sp < ep and txt[sp] == ?\s
  128. end
  129. res.join
  130. end
  131. ##
  132. # :section: Visitor
  133. def start_accepting
  134. @res = []
  135. @in_list_entry = []
  136. @list = []
  137. end
  138. def end_accepting
  139. @res.join
  140. end
  141. def accept_paragraph(paragraph)
  142. @res << annotate("<p>") + "\n"
  143. @res << wrap(convert_flow(@am.flow(paragraph.text)))
  144. @res << annotate("</p>") + "\n"
  145. end
  146. def accept_verbatim(verbatim)
  147. @res << annotate("<pre>") << "\n"
  148. @res << CGI.escapeHTML(verbatim.text)
  149. @res << annotate("</pre>") << "\n"
  150. end
  151. def accept_rule(rule)
  152. size = rule.weight
  153. size = 10 if size > 10
  154. @res << "<hr style=\"height: #{size}px\"></hr>"
  155. end
  156. def accept_list_start(list)
  157. @list << list.type
  158. @res << html_list_name(list.type, true) << "\n"
  159. @in_list_entry.push false
  160. end
  161. def accept_list_end(list)
  162. @list.pop
  163. if tag = @in_list_entry.pop
  164. @res << annotate(tag) << "\n"
  165. end
  166. @res << html_list_name(list.type, false) << "\n"
  167. end
  168. def accept_list_item_start(list_item)
  169. if tag = @in_list_entry.last
  170. @res << annotate(tag) << "\n"
  171. end
  172. @res << list_item_start(list_item, @list.last)
  173. end
  174. def accept_list_item_end(list_item)
  175. @in_list_entry[-1] = list_end_for(@list.last)
  176. end
  177. def accept_blank_line(blank_line)
  178. # @res << annotate("<p />") << "\n"
  179. end
  180. def accept_heading(heading)
  181. @res << convert_heading(heading.level, @am.flow(heading.text))
  182. end
  183. private
  184. ##
  185. # Converts string +item+
  186. def convert_string(item)
  187. in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
  188. end
  189. ##
  190. # Escapes HTML in +item+
  191. def convert_string_simple(item)
  192. CGI.escapeHTML item
  193. end
  194. ##
  195. # Converts ampersand, dashes, elipsis, quotes, copyright and registered
  196. # trademark symbols to HTML escaped Unicode.
  197. def convert_string_fancy(item)
  198. # convert ampersand before doing anything else
  199. item.gsub(/&/, '&amp;').
  200. # convert -- to em-dash, (-- to en-dash)
  201. gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
  202. # convert ... to elipsis (and make sure .... becomes .<elipsis>)
  203. gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
  204. # convert single closing quote
  205. gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;'). # }
  206. gsub(%r{\'(?=\W|s\b)}, '&#8217;').
  207. # convert single opening quote
  208. gsub(/'/, '&#8216;').
  209. # convert double closing quote
  210. gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1&#8221;'). # }
  211. # convert double opening quote
  212. gsub(/"/, '&#8220;').
  213. # convert copyright
  214. gsub(/\(c\)/, '&#169;').
  215. # convert registered trademark
  216. gsub(/\(r\)/, '&#174;')
  217. end
  218. ##
  219. # Converts headings to hN elements
  220. def convert_heading(level, flow)
  221. [annotate("<h#{level}>"),
  222. convert_flow(flow),
  223. annotate("</h#{level}>\n")].join
  224. end
  225. ##
  226. # Determins the HTML list element for +list_type+ and +open_tag+
  227. def html_list_name(list_type, open_tag)
  228. tags = LIST_TYPE_TO_HTML[list_type]
  229. raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
  230. annotate tags[open_tag ? 0 : 1]
  231. end
  232. ##
  233. # Starts a list item
  234. def list_item_start(list_item, list_type)
  235. case list_type
  236. when :BULLET, :LALPHA, :NUMBER, :UALPHA then
  237. annotate("<li>")
  238. when :LABEL then
  239. annotate("<dt>") +
  240. convert_flow(@am.flow(list_item.label)) +
  241. annotate("</dt>") +
  242. annotate("<dd>")
  243. when :NOTE then
  244. annotate("<tr>") +
  245. annotate("<td valign=\"top\">") +
  246. convert_flow(@am.flow(list_item.label)) +
  247. annotate("</td>") +
  248. annotate("<td>")
  249. else
  250. raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
  251. end
  252. end
  253. ##
  254. # Ends a list item
  255. def list_end_for(list_type)
  256. case list_type
  257. when :BULLET, :LALPHA, :NUMBER, :UALPHA then
  258. "</li>"
  259. when :LABEL then
  260. "</dd>"
  261. when :NOTE then
  262. "</td></tr>"
  263. else
  264. raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
  265. end
  266. end
  267. end