PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Util/IronRuby/lib/ruby/1.8/rdoc/markup/simple_markup/to_html.rb

http://github.com/IronLanguages/main
Ruby | 289 lines | 207 code | 54 blank | 28 comment | 20 complexity | 33af6b3059559ba4878f6abe0f05d63b MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. require 'rdoc/markup/simple_markup/fragments'
  2. require 'rdoc/markup/simple_markup/inline'
  3. require 'cgi'
  4. module SM
  5. class ToHtml
  6. LIST_TYPE_TO_HTML = {
  7. ListBase::BULLET => [ "<ul>", "</ul>" ],
  8. ListBase::NUMBER => [ "<ol>", "</ol>" ],
  9. ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
  10. ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
  11. ListBase::LABELED => [ "<dl>", "</dl>" ],
  12. ListBase::NOTE => [ "<table>", "</table>" ],
  13. }
  14. InlineTag = Struct.new(:bit, :on, :off)
  15. def initialize
  16. init_tags
  17. end
  18. ##
  19. # Set up the standard mapping of attributes to HTML tags
  20. #
  21. def init_tags
  22. @attr_tags = [
  23. InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
  24. InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
  25. InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
  26. ]
  27. end
  28. ##
  29. # Add a new set of HTML tags for an attribute. We allow
  30. # separate start and end tags for flexibility
  31. #
  32. def add_tag(name, start, stop)
  33. @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
  34. end
  35. ##
  36. # Given an HTML tag, decorate it with class information
  37. # and the like if required. This is a no-op in the base
  38. # class, but is overridden in HTML output classes that
  39. # implement style sheets
  40. def annotate(tag)
  41. tag
  42. end
  43. ##
  44. # Here's the client side of the visitor pattern
  45. def start_accepting
  46. @res = ""
  47. @in_list_entry = []
  48. end
  49. def end_accepting
  50. @res
  51. end
  52. def accept_paragraph(am, fragment)
  53. @res << annotate("<p>") + "\n"
  54. @res << wrap(convert_flow(am.flow(fragment.txt)))
  55. @res << annotate("</p>") + "\n"
  56. end
  57. def accept_verbatim(am, fragment)
  58. @res << annotate("<pre>") + "\n"
  59. @res << CGI.escapeHTML(fragment.txt)
  60. @res << annotate("</pre>") << "\n"
  61. end
  62. def accept_rule(am, fragment)
  63. size = fragment.param
  64. size = 10 if size > 10
  65. @res << "<hr size=\"#{size}\"></hr>"
  66. end
  67. def accept_list_start(am, fragment)
  68. @res << html_list_name(fragment.type, true) <<"\n"
  69. @in_list_entry.push false
  70. end
  71. def accept_list_end(am, fragment)
  72. if tag = @in_list_entry.pop
  73. @res << annotate(tag) << "\n"
  74. end
  75. @res << html_list_name(fragment.type, false) <<"\n"
  76. end
  77. def accept_list_item(am, fragment)
  78. if tag = @in_list_entry.last
  79. @res << annotate(tag) << "\n"
  80. end
  81. @res << list_item_start(am, fragment)
  82. @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
  83. @in_list_entry[-1] = list_end_for(fragment.type)
  84. end
  85. def accept_blank_line(am, fragment)
  86. # @res << annotate("<p />") << "\n"
  87. end
  88. def accept_heading(am, fragment)
  89. @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
  90. end
  91. # This is a higher speed (if messier) version of wrap
  92. def wrap(txt, line_len = 76)
  93. res = ""
  94. sp = 0
  95. ep = txt.length
  96. while sp < ep
  97. # scan back for a space
  98. p = sp + line_len - 1
  99. if p >= ep
  100. p = ep
  101. else
  102. while p > sp and txt[p] != ?\s
  103. p -= 1
  104. end
  105. if p <= sp
  106. p = sp + line_len
  107. while p < ep and txt[p] != ?\s
  108. p += 1
  109. end
  110. end
  111. end
  112. res << txt[sp...p] << "\n"
  113. sp = p
  114. sp += 1 while sp < ep and txt[sp] == ?\s
  115. end
  116. res
  117. end
  118. #######################################################################
  119. private
  120. #######################################################################
  121. def on_tags(res, item)
  122. attr_mask = item.turn_on
  123. return if attr_mask.zero?
  124. @attr_tags.each do |tag|
  125. if attr_mask & tag.bit != 0
  126. res << annotate(tag.on)
  127. end
  128. end
  129. end
  130. def off_tags(res, item)
  131. attr_mask = item.turn_off
  132. return if attr_mask.zero?
  133. @attr_tags.reverse_each do |tag|
  134. if attr_mask & tag.bit != 0
  135. res << annotate(tag.off)
  136. end
  137. end
  138. end
  139. def convert_flow(flow)
  140. res = ""
  141. flow.each do |item|
  142. case item
  143. when String
  144. res << convert_string(item)
  145. when AttrChanger
  146. off_tags(res, item)
  147. on_tags(res, item)
  148. when Special
  149. res << convert_special(item)
  150. else
  151. raise "Unknown flow element: #{item.inspect}"
  152. end
  153. end
  154. res
  155. end
  156. # some of these patterns are taken from SmartyPants...
  157. def convert_string(item)
  158. CGI.escapeHTML(item).
  159. # convert -- to em-dash, (-- to en-dash)
  160. gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
  161. # convert ... to elipsis (and make sure .... becomes .<elipsis>)
  162. gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
  163. # convert single closing quote
  164. gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
  165. gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
  166. # convert single opening quote
  167. gsub(/'/, '&#8216;').
  168. # convert double closing quote
  169. gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
  170. # convert double opening quote
  171. gsub(/'/, '&#8220;').
  172. # convert copyright
  173. gsub(/\(c\)/, '&#169;').
  174. # convert and registered trademark
  175. gsub(/\(r\)/, '&#174;')
  176. end
  177. def convert_special(special)
  178. handled = false
  179. Attribute.each_name_of(special.type) do |name|
  180. method_name = "handle_special_#{name}"
  181. if self.respond_to? method_name
  182. special.text = send(method_name, special)
  183. handled = true
  184. end
  185. end
  186. raise "Unhandled special: #{special}" unless handled
  187. special.text
  188. end
  189. def convert_heading(level, flow)
  190. res =
  191. annotate("<h#{level}>") +
  192. convert_flow(flow) +
  193. annotate("</h#{level}>\n")
  194. end
  195. def html_list_name(list_type, is_open_tag)
  196. tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
  197. annotate(tags[ is_open_tag ? 0 : 1])
  198. end
  199. def list_item_start(am, fragment)
  200. case fragment.type
  201. when ListBase::BULLET, ListBase::NUMBER
  202. annotate("<li>")
  203. when ListBase::UPPERALPHA
  204. annotate("<li type=\"A\">")
  205. when ListBase::LOWERALPHA
  206. annotate("<li type=\"a\">")
  207. when ListBase::LABELED
  208. annotate("<dt>") +
  209. convert_flow(am.flow(fragment.param)) +
  210. annotate("</dt>") +
  211. annotate("<dd>")
  212. when ListBase::NOTE
  213. annotate("<tr>") +
  214. annotate("<td valign=\"top\">") +
  215. convert_flow(am.flow(fragment.param)) +
  216. annotate("</td>") +
  217. annotate("<td>")
  218. else
  219. raise "Invalid list type"
  220. end
  221. end
  222. def list_end_for(fragment_type)
  223. case fragment_type
  224. when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
  225. "</li>"
  226. when ListBase::LABELED
  227. "</dd>"
  228. when ListBase::NOTE
  229. "</td></tr>"
  230. else
  231. raise "Invalid list type"
  232. end
  233. end
  234. end
  235. end