PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/jruby-1.1.6RC1/lib/ruby/1.8/rdoc/ri/ri_formatter.rb

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