PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/benchmark/app/rdoc-2.4.3/lib/rdoc/ri/formatter.rb

http://github.com/rubinius/rubinius
Ruby | 654 lines | 478 code | 127 blank | 49 comment | 34 complexity | cd52ada6b10eed08d346ef9e2af179bd 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/ri'
  2. require 'rdoc/markup'
  3. class RDoc::RI::Formatter
  4. attr_writer :indent
  5. attr_accessor :output
  6. FORMATTERS = { }
  7. def self.for(name)
  8. FORMATTERS[name.downcase]
  9. end
  10. def self.list
  11. FORMATTERS.keys.sort.join ", "
  12. end
  13. def initialize(output, width, indent)
  14. @output = output
  15. @width = width
  16. @indent = indent
  17. @original_indent = indent.dup
  18. end
  19. def draw_line(label=nil)
  20. len = @width
  21. len -= (label.size + 1) if label
  22. if len > 0 then
  23. @output.print '-' * len
  24. if label
  25. @output.print ' '
  26. bold_print label
  27. end
  28. @output.puts
  29. else
  30. @output.print '-' * @width
  31. @output.puts
  32. @output.puts label
  33. end
  34. end
  35. def indent
  36. return @indent unless block_given?
  37. begin
  38. indent = @indent.dup
  39. @indent += @original_indent
  40. yield
  41. ensure
  42. @indent = indent
  43. end
  44. end
  45. def wrap(txt, prefix=@indent, linelen=@width)
  46. return unless txt && !txt.empty?
  47. work = conv_markup(txt)
  48. textLen = linelen - prefix.length
  49. patt = Regexp.new("^(.{0,#{textLen}})[ \n]")
  50. next_prefix = prefix.tr("^ ", " ")
  51. res = []
  52. while work.length > textLen
  53. if work =~ patt
  54. res << $1
  55. work.slice!(0, $&.length)
  56. else
  57. res << work.slice!(0, textLen)
  58. end
  59. end
  60. res << work if work.length.nonzero?
  61. @output.puts(prefix + res.join("\n" + next_prefix))
  62. end
  63. def blankline
  64. @output.puts
  65. end
  66. ##
  67. # Called when we want to ensure a new 'wrap' starts on a newline. Only
  68. # needed for HtmlFormatter, because the rest do their own line breaking.
  69. def break_to_newline
  70. end
  71. def bold_print(txt)
  72. @output.print txt
  73. end
  74. def raw_print_line(txt)
  75. @output.print txt
  76. end
  77. ##
  78. # Convert HTML entities back to ASCII
  79. def conv_html(txt)
  80. txt = txt.gsub(/&gt;/, '>')
  81. txt.gsub!(/&lt;/, '<')
  82. txt.gsub!(/&quot;/, '"')
  83. txt.gsub!(/&amp;/, '&')
  84. txt
  85. end
  86. ##
  87. # Convert markup into display form
  88. def conv_markup(txt)
  89. txt = txt.gsub(%r{<tt>(.*?)</tt>}, '+\1+')
  90. txt.gsub!(%r{<code>(.*?)</code>}, '+\1+')
  91. txt.gsub!(%r{<b>(.*?)</b>}, '*\1*')
  92. txt.gsub!(%r{<em>(.*?)</em>}, '_\1_')
  93. txt
  94. end
  95. def display_list(list)
  96. case list.type
  97. when :BULLET
  98. prefixer = proc { |ignored| @indent + "* " }
  99. when :NUMBER, :UPPERALPHA, :LOWERALPHA then
  100. start = case list.type
  101. when :NUMBER then 1
  102. when :UPPERALPHA then 'A'
  103. when :LOWERALPHA then 'a'
  104. end
  105. prefixer = proc do |ignored|
  106. res = @indent + "#{start}.".ljust(4)
  107. start = start.succ
  108. res
  109. end
  110. when :LABELED, :NOTE then
  111. longest = 0
  112. list.contents.each do |item|
  113. if RDoc::Markup::Flow::LI === item and item.label.length > longest then
  114. longest = item.label.length
  115. end
  116. end
  117. longest += 1
  118. prefixer = proc { |li| @indent + li.label.ljust(longest) }
  119. else
  120. raise ArgumentError, "unknown list type #{list.type}"
  121. end
  122. list.contents.each do |item|
  123. if RDoc::Markup::Flow::LI === item then
  124. prefix = prefixer.call item
  125. display_flow_item item, prefix
  126. else
  127. display_flow_item item
  128. end
  129. end
  130. end
  131. def display_flow_item(item, prefix = @indent)
  132. case item
  133. when RDoc::Markup::Flow::P, RDoc::Markup::Flow::LI
  134. wrap(conv_html(item.body), prefix)
  135. blankline
  136. when RDoc::Markup::Flow::LIST
  137. display_list(item)
  138. when RDoc::Markup::Flow::VERB
  139. display_verbatim_flow_item(item, @indent)
  140. when RDoc::Markup::Flow::H
  141. display_heading(conv_html(item.text), item.level, @indent)
  142. when RDoc::Markup::Flow::RULE
  143. draw_line
  144. else
  145. raise RDoc::Error, "Unknown flow element: #{item.class}"
  146. end
  147. end
  148. def display_verbatim_flow_item(item, prefix=@indent)
  149. item.body.split(/\n/).each do |line|
  150. @output.print @indent, conv_html(line), "\n"
  151. end
  152. blankline
  153. end
  154. def display_heading(text, level, indent)
  155. text = strip_attributes text
  156. case level
  157. when 1 then
  158. ul = "=" * text.length
  159. @output.puts
  160. @output.puts text.upcase
  161. @output.puts ul
  162. when 2 then
  163. ul = "-" * text.length
  164. @output.puts
  165. @output.puts text
  166. @output.puts ul
  167. else
  168. @output.print indent, text, "\n"
  169. end
  170. @output.puts
  171. end
  172. def display_flow(flow)
  173. flow.each do |f|
  174. display_flow_item(f)
  175. end
  176. end
  177. def strip_attributes(text)
  178. text.gsub(/(<\/?(?:b|code|em|i|tt)>)/, '')
  179. end
  180. end
  181. ##
  182. # Handle text with attributes. We're a base class: there are different
  183. # presentation classes (one, for example, uses overstrikes to handle bold and
  184. # underlining, while another using ANSI escape sequences.
  185. class RDoc::RI::AttributeFormatter < RDoc::RI::Formatter
  186. BOLD = 1
  187. ITALIC = 2
  188. CODE = 4
  189. ATTR_MAP = {
  190. "b" => BOLD,
  191. "code" => CODE,
  192. "em" => ITALIC,
  193. "i" => ITALIC,
  194. "tt" => CODE
  195. }
  196. AttrChar = Struct.new :char, :attr
  197. class AttributeString
  198. attr_reader :txt
  199. def initialize
  200. @txt = []
  201. @optr = 0
  202. end
  203. def <<(char)
  204. @txt << char
  205. end
  206. def empty?
  207. @optr >= @txt.length
  208. end
  209. # accept non space, then all following spaces
  210. def next_word
  211. start = @optr
  212. len = @txt.length
  213. while @optr < len && @txt[@optr].char != " "
  214. @optr += 1
  215. end
  216. while @optr < len && @txt[@optr].char == " "
  217. @optr += 1
  218. end
  219. @txt[start...@optr]
  220. end
  221. end
  222. ##
  223. # Overrides base class. Looks for <tt>...</tt> etc sequences and generates
  224. # an array of AttrChars. This array is then used as the basis for the
  225. # split.
  226. def wrap(txt, prefix=@indent, linelen=@width)
  227. return unless txt && !txt.empty?
  228. txt = add_attributes_to(txt)
  229. next_prefix = prefix.tr("^ ", " ")
  230. linelen -= prefix.size
  231. line = []
  232. until txt.empty?
  233. word = txt.next_word
  234. if word.size + line.size > linelen
  235. write_attribute_text(prefix, line)
  236. prefix = next_prefix
  237. line = []
  238. end
  239. line.concat(word)
  240. end
  241. write_attribute_text(prefix, line) if line.length > 0
  242. end
  243. protected
  244. def write_attribute_text(prefix, line)
  245. @output.print prefix
  246. line.each do |achar|
  247. @output.print achar.char
  248. end
  249. @output.puts
  250. end
  251. def bold_print(txt)
  252. @output.print txt
  253. end
  254. private
  255. def add_attributes_to(txt)
  256. tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
  257. text = AttributeString.new
  258. attributes = 0
  259. tokens.each do |tok|
  260. case tok
  261. when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0)
  262. when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0)
  263. else
  264. tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)}
  265. end
  266. end
  267. text
  268. end
  269. end
  270. ##
  271. # This formatter generates overstrike-style formatting, which works with
  272. # pagers such as man and less.
  273. class RDoc::RI::OverstrikeFormatter < RDoc::RI::AttributeFormatter
  274. BS = "\C-h"
  275. def write_attribute_text(prefix, line)
  276. @output.print prefix
  277. line.each do |achar|
  278. attr = achar.attr
  279. @output.print "_", BS if (attr & (ITALIC + CODE)) != 0
  280. @output.print achar.char, BS if (attr & BOLD) != 0
  281. @output.print achar.char
  282. end
  283. @output.puts
  284. end
  285. ##
  286. # Draw a string in bold
  287. def bold_print(text)
  288. text.split(//).each do |ch|
  289. @output.print ch, BS, ch
  290. end
  291. end
  292. end
  293. ##
  294. # This formatter uses ANSI escape sequences to colorize stuff works with
  295. # pagers such as man and less.
  296. class RDoc::RI::AnsiFormatter < RDoc::RI::AttributeFormatter
  297. def initialize(*args)
  298. super
  299. @output.print "\033[0m"
  300. end
  301. def write_attribute_text(prefix, line)
  302. @output.print prefix
  303. curr_attr = 0
  304. line.each do |achar|
  305. attr = achar.attr
  306. if achar.attr != curr_attr
  307. update_attributes(achar.attr)
  308. curr_attr = achar.attr
  309. end
  310. @output.print achar.char
  311. end
  312. update_attributes(0) unless curr_attr.zero?
  313. @output.puts
  314. end
  315. def bold_print(txt)
  316. @output.print "\033[1m#{txt}\033[m"
  317. end
  318. HEADINGS = {
  319. 1 => ["\033[1;32m", "\033[m"],
  320. 2 => ["\033[4;32m", "\033[m"],
  321. 3 => ["\033[32m", "\033[m"],
  322. }
  323. def display_heading(text, level, indent)
  324. level = 3 if level > 3
  325. heading = HEADINGS[level]
  326. @output.print indent
  327. @output.print heading[0]
  328. @output.print strip_attributes(text)
  329. @output.puts heading[1]
  330. end
  331. private
  332. ATTR_MAP = {
  333. BOLD => "1",
  334. ITALIC => "33",
  335. CODE => "36"
  336. }
  337. def update_attributes(attr)
  338. str = "\033["
  339. for quality in [ BOLD, ITALIC, CODE]
  340. unless (attr & quality).zero?
  341. str << ATTR_MAP[quality]
  342. end
  343. end
  344. @output.print str, "m"
  345. end
  346. end
  347. ##
  348. # This formatter uses HTML.
  349. class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter
  350. ##
  351. # We depend on HTML4-conforming user agents to ignore an empty p element
  352. def blankline
  353. @output.puts '<p />'
  354. end
  355. ##
  356. # Emboldens +text+
  357. def bold_print(text)
  358. tag("b") { text }
  359. end
  360. ##
  361. # Outputs a forced line break element
  362. def break_to_newline
  363. @output.puts '<br />'
  364. end
  365. ##
  366. # Outputs heading elements for +text+ with +level+ up to 4. Ignores
  367. # +indent+.
  368. def display_heading(text, level, indent)
  369. level = 4 if level > 4
  370. tag("h#{level}") { text }
  371. @output.puts
  372. end
  373. ##
  374. # Outputs +list+ which is displayed as follows:
  375. #
  376. # BULLET:: unordered list
  377. # NUMBER:: ordered list
  378. # LABELED:: definition list
  379. # NOTE:: table
  380. def display_list(list)
  381. case list.type
  382. when :BULLET then
  383. list_type = "ul"
  384. prefixer = proc { |ignored| '<li>' }
  385. suffix = '</li>'
  386. when :NUMBER, :UPPERALPHA, :LOWERALPHA then
  387. list_type = "ol"
  388. prefixer = proc { |ignored| '<li>' }
  389. suffix = '</li>'
  390. when :LABELED then
  391. list_type = "dl"
  392. prefixer = proc do |li|
  393. "<dt><b>#{escape li.label}</b></dt><dd>"
  394. end
  395. suffix = '</dd>'
  396. when :NOTE then
  397. list_type = "table"
  398. prefixer = proc do |li|
  399. %{<tr valign="top"><td>#{li.label.gsub(/ /, '&nbsp;')}</td><td>}
  400. end
  401. suffix = '</td></tr>'
  402. else
  403. fail "unknown list type"
  404. end
  405. @output.print "<#{list_type}>"
  406. list.contents.each do |item|
  407. if item.kind_of? RDoc::Markup::Flow::LI
  408. prefix = prefixer.call item
  409. @output.print prefix
  410. display_flow_item item, prefix
  411. @output.print suffix
  412. else
  413. display_flow_item item
  414. end
  415. end
  416. @output.print "</#{list_type}>"
  417. end
  418. ##
  419. # Outputs a preformatted section for +item+. +prefix+ is ignored.
  420. def display_verbatim_flow_item(item, prefix=@indent)
  421. @output.print '<pre>'
  422. item.body.split(/\n/).each do |line|
  423. @output.puts escape(line)
  424. end
  425. @output.puts '</pre>'
  426. end
  427. ##
  428. # Outputs a horizontal rule element, optionally labeled above with +label+ in
  429. # bold.
  430. def draw_line(label = nil)
  431. bold_print label if label
  432. @output.puts "<hr />"
  433. end
  434. def write_attribute_text(prefix, line)
  435. curr_attr = 0
  436. line.each do |achar|
  437. attr = achar.attr
  438. if achar.attr != curr_attr then
  439. update_attributes curr_attr, achar.attr
  440. curr_attr = achar.attr
  441. end
  442. @output.print escape(achar.char)
  443. end
  444. update_attributes curr_attr, 0 unless curr_attr.zero?
  445. end
  446. private
  447. ATTR_MAP = {
  448. BOLD => "b>",
  449. ITALIC => "i>",
  450. CODE => "tt>"
  451. }
  452. def update_attributes(current, wanted)
  453. str = ""
  454. # first turn off unwanted ones
  455. off = current & ~wanted
  456. for quality in [ BOLD, ITALIC, CODE]
  457. if (off & quality) > 0
  458. str << "</" + ATTR_MAP[quality]
  459. end
  460. end
  461. # now turn on wanted
  462. for quality in [ BOLD, ITALIC, CODE]
  463. unless (wanted & quality).zero?
  464. str << "<" << ATTR_MAP[quality]
  465. end
  466. end
  467. @output.print str
  468. end
  469. def tag(code)
  470. @output.print("<#{code}>")
  471. @output.print(yield)
  472. @output.print("</#{code}>")
  473. end
  474. def escape(str)
  475. str = str.gsub(/&/n, '&amp;')
  476. str.gsub!(/\"/n, '&quot;')
  477. str.gsub!(/>/n, '&gt;')
  478. str.gsub!(/</n, '&lt;')
  479. str
  480. end
  481. end
  482. ##
  483. # This formatter reduces extra lines for a simpler output. It improves way
  484. # output looks for tools like IRC bots.
  485. class RDoc::RI::SimpleFormatter < RDoc::RI::Formatter
  486. ##
  487. # No extra blank lines
  488. def blankline
  489. end
  490. ##
  491. # Display labels only, no lines
  492. def draw_line(label=nil)
  493. unless label.nil? then
  494. bold_print(label)
  495. @output.puts
  496. end
  497. end
  498. ##
  499. # Place heading level indicators inline with heading.
  500. def display_heading(text, level, indent)
  501. text = strip_attributes(text)
  502. case level
  503. when 1
  504. @output.puts "= " + text.upcase
  505. when 2
  506. @output.puts "-- " + text
  507. else
  508. @output.print indent, text, "\n"
  509. end
  510. end
  511. end
  512. RDoc::RI::Formatter::FORMATTERS['plain'] = RDoc::RI::Formatter
  513. RDoc::RI::Formatter::FORMATTERS['simple'] = RDoc::RI::SimpleFormatter
  514. RDoc::RI::Formatter::FORMATTERS['bs'] = RDoc::RI::OverstrikeFormatter
  515. RDoc::RI::Formatter::FORMATTERS['ansi'] = RDoc::RI::AnsiFormatter
  516. RDoc::RI::Formatter::FORMATTERS['html'] = RDoc::RI::HtmlFormatter