source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rdoc/ri/ri_formatter.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

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