/Util/IronRuby/lib/ruby/1.8/rdoc/ri/ri_formatter.rb
Ruby | 672 lines | 505 code | 123 blank | 44 comment | 35 complexity | f6f85e55107da4f50f54190dbce82189 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- module RI
- class TextFormatter
- attr_reader :indent
-
- def initialize(options, indent)
- @options = options
- @width = options.width
- @indent = indent
- end
-
-
- ######################################################################
-
- def draw_line(label=nil)
- len = @width
- len -= (label.size+1) if label
- print "-"*len
- if label
- print(" ")
- bold_print(label)
- end
- puts
- end
-
- ######################################################################
-
- def wrap(txt, prefix=@indent, linelen=@width)
- return unless txt && !txt.empty?
- work = conv_markup(txt)
- textLen = linelen - prefix.length
- patt = Regexp.new("^(.{0,#{textLen}})[ \n]")
- next_prefix = prefix.tr("^ ", " ")
- res = []
- while work.length > textLen
- if work =~ patt
- res << $1
- work.slice!(0, $&.length)
- else
- res << work.slice!(0, textLen)
- end
- end
- res << work if work.length.nonzero?
- puts(prefix + res.join("\n" + next_prefix))
- end
- ######################################################################
- def blankline
- puts
- end
-
- ######################################################################
- # called when we want to ensure a nbew 'wrap' starts on a newline
- # Only needed for HtmlFormatter, because the rest do their
- # own line breaking
- def break_to_newline
- end
-
- ######################################################################
- def bold_print(txt)
- print txt
- end
- ######################################################################
- def raw_print_line(txt)
- puts txt
- end
- ######################################################################
- # convert HTML entities back to ASCII
- def conv_html(txt)
- txt.
- gsub(/>/, '>').
- gsub(/</, '<').
- gsub(/"/, '"').
- gsub(/&/, '&')
-
- end
- # convert markup into display form
- def conv_markup(txt)
- txt.
- gsub(%r{<tt>(.*?)</tt>}) { "+#$1+" } .
- gsub(%r{<code>(.*?)</code>}) { "+#$1+" } .
- gsub(%r{<b>(.*?)</b>}) { "*#$1*" } .
- gsub(%r{<em>(.*?)</em>}) { "_#$1_" }
- end
- ######################################################################
- def display_list(list)
- case list.type
- when SM::ListBase::BULLET
- prefixer = proc { |ignored| @indent + "* " }
- when SM::ListBase::NUMBER,
- SM::ListBase::UPPERALPHA,
- SM::ListBase::LOWERALPHA
- start = case list.type
- when SM::ListBase::NUMBER then 1
- when SM::ListBase::UPPERALPHA then 'A'
- when SM::ListBase::LOWERALPHA then 'a'
- end
- prefixer = proc do |ignored|
- res = @indent + "#{start}.".ljust(4)
- start = start.succ
- res
- end
-
- when SM::ListBase::LABELED
- prefixer = proc do |li|
- li.label
- end
- when SM::ListBase::NOTE
- longest = 0
- list.contents.each do |item|
- if item.kind_of?(SM::Flow::LI) && item.label.length > longest
- longest = item.label.length
- end
- end
- prefixer = proc do |li|
- @indent + li.label.ljust(longest+1)
- end
- else
- fail "unknown list type"
- end
- list.contents.each do |item|
- if item.kind_of? SM::Flow::LI
- prefix = prefixer.call(item)
- display_flow_item(item, prefix)
- else
- display_flow_item(item)
- end
- end
- end
- ######################################################################
- def display_flow_item(item, prefix=@indent)
- case item
- when SM::Flow::P, SM::Flow::LI
- wrap(conv_html(item.body), prefix)
- blankline
-
- when SM::Flow::LIST
- display_list(item)
- when SM::Flow::VERB
- display_verbatim_flow_item(item, @indent)
- when SM::Flow::H
- display_heading(conv_html(item.text), item.level, @indent)
- when SM::Flow::RULE
- draw_line
- else
- fail "Unknown flow element: #{item.class}"
- end
- end
- ######################################################################
- def display_verbatim_flow_item(item, prefix=@indent)
- item.body.split(/\n/).each do |line|
- print @indent, conv_html(line), "\n"
- end
- blankline
- end
- ######################################################################
- def display_heading(text, level, indent)
- text = strip_attributes(text)
- case level
- when 1
- ul = "=" * text.length
- puts
- puts text.upcase
- puts ul
- # puts
-
- when 2
- ul = "-" * text.length
- puts
- puts text
- puts ul
- # puts
- else
- print indent, text, "\n"
- end
- end
- def display_flow(flow)
- flow.each do |f|
- display_flow_item(f)
- end
- end
- def strip_attributes(txt)
- tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
- text = []
- attributes = 0
- tokens.each do |tok|
- case tok
- when %r{^</(\w+)>$}, %r{^<(\w+)>$}
- ;
- else
- text << tok
- end
- end
- text.join
- end
- end
-
-
- ######################################################################
- # Handle text with attributes. We're a base class: there are
- # different presentation classes (one, for example, uses overstrikes
- # to handle bold and underlining, while another using ANSI escape
- # sequences
-
- class AttributeFormatter < TextFormatter
-
- BOLD = 1
- ITALIC = 2
- CODE = 4
- ATTR_MAP = {
- "b" => BOLD,
- "code" => CODE,
- "em" => ITALIC,
- "i" => ITALIC,
- "tt" => CODE
- }
- # TODO: struct?
- class AttrChar
- attr_reader :char
- attr_reader :attr
- def initialize(char, attr)
- @char = char
- @attr = attr
- end
- end
-
- class AttributeString
- attr_reader :txt
- def initialize
- @txt = []
- @optr = 0
- end
- def <<(char)
- @txt << char
- end
- def empty?
- @optr >= @txt.length
- end
- # accept non space, then all following spaces
- def next_word
- start = @optr
- len = @txt.length
- while @optr < len && @txt[@optr].char != " "
- @optr += 1
- end
- while @optr < len && @txt[@optr].char == " "
- @optr += 1
- end
- @txt[start...@optr]
- end
- end
- ######################################################################
- # overrides base class. Looks for <tt>...</tt> etc sequences
- # and generates an array of AttrChars. This array is then used
- # as the basis for the split
- def wrap(txt, prefix=@indent, linelen=@width)
- return unless txt && !txt.empty?
- txt = add_attributes_to(txt)
- next_prefix = prefix.tr("^ ", " ")
- linelen -= prefix.size
- line = []
- until txt.empty?
- word = txt.next_word
- if word.size + line.size > linelen
- write_attribute_text(prefix, line)
- prefix = next_prefix
- line = []
- end
- line.concat(word)
- end
- write_attribute_text(prefix, line) if line.length > 0
- end
- protected
- # overridden in specific formatters
- def write_attribute_text(prefix, line)
- print prefix
- line.each do |achar|
- print achar.char
- end
- puts
- end
- # again, overridden
- def bold_print(txt)
- print txt
- end
- private
- def add_attributes_to(txt)
- tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
- text = AttributeString.new
- attributes = 0
- tokens.each do |tok|
- case tok
- when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0)
- when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0)
- else
- tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)}
- end
- end
- text
- end
- end
- ##################################################
-
- # This formatter generates overstrike-style formatting, which
- # works with pagers such as man and less.
- class OverstrikeFormatter < AttributeFormatter
- BS = "\C-h"
- def write_attribute_text(prefix, line)
- print prefix
- line.each do |achar|
- attr = achar.attr
- if (attr & (ITALIC+CODE)) != 0
- print "_", BS
- end
- if (attr & BOLD) != 0
- print achar.char, BS
- end
- print achar.char
- end
- puts
- end
- # draw a string in bold
- def bold_print(text)
- text.split(//).each do |ch|
- print ch, BS, ch
- end
- end
- end
- ##################################################
-
- # This formatter uses ANSI escape sequences
- # to colorize stuff
- # works with pages such as man and less.
- class AnsiFormatter < AttributeFormatter
- def initialize(*args)
- print "\033[0m"
- super
- end
- def write_attribute_text(prefix, line)
- print prefix
- curr_attr = 0
- line.each do |achar|
- attr = achar.attr
- if achar.attr != curr_attr
- update_attributes(achar.attr)
- curr_attr = achar.attr
- end
- print achar.char
- end
- update_attributes(0) unless curr_attr.zero?
- puts
- end
- def bold_print(txt)
- print "\033[1m#{txt}\033[m"
- end
- HEADINGS = {
- 1 => [ "\033[1;32m", "\033[m" ] ,
- 2 => ["\033[4;32m", "\033[m" ],
- 3 => ["\033[32m", "\033[m" ]
- }
- def display_heading(text, level, indent)
- level = 3 if level > 3
- heading = HEADINGS[level]
- print indent
- print heading[0]
- print strip_attributes(text)
- puts heading[1]
- end
-
- private
- ATTR_MAP = {
- BOLD => "1",
- ITALIC => "33",
- CODE => "36"
- }
- def update_attributes(attr)
- str = "\033["
- for quality in [ BOLD, ITALIC, CODE]
- unless (attr & quality).zero?
- str << ATTR_MAP[quality]
- end
- end
- print str, "m"
- end
- end
- ##################################################
-
- # This formatter uses HTML.
- class HtmlFormatter < AttributeFormatter
- def initialize(*args)
- super
- end
- def write_attribute_text(prefix, line)
- curr_attr = 0
- line.each do |achar|
- attr = achar.attr
- if achar.attr != curr_attr
- update_attributes(curr_attr, achar.attr)
- curr_attr = achar.attr
- end
- print(escape(achar.char))
- end
- update_attributes(curr_attr, 0) unless curr_attr.zero?
- end
- def draw_line(label=nil)
- if label != nil
- bold_print(label)
- end
- puts("<hr>")
- end
- def bold_print(txt)
- tag("b") { txt }
- end
- def blankline()
- puts("<p>")
- end
- def break_to_newline
- puts("<br>")
- end
- def display_heading(text, level, indent)
- level = 4 if level > 4
- tag("h#{level}") { text }
- puts
- end
-
- ######################################################################
- def display_list(list)
- case list.type
- when SM::ListBase::BULLET
- list_type = "ul"
- prefixer = proc { |ignored| "<li>" }
- when SM::ListBase::NUMBER,
- SM::ListBase::UPPERALPHA,
- SM::ListBase::LOWERALPHA
- list_type = "ol"
- prefixer = proc { |ignored| "<li>" }
-
- when SM::ListBase::LABELED
- list_type = "dl"
- prefixer = proc do |li|
- "<dt><b>" + escape(li.label) + "</b><dd>"
- end
- when SM::ListBase::NOTE
- list_type = "table"
- prefixer = proc do |li|
- %{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>}
- end
- else
- fail "unknown list type"
- end
- print "<#{list_type}>"
- list.contents.each do |item|
- if item.kind_of? SM::Flow::LI
- prefix = prefixer.call(item)
- print prefix
- display_flow_item(item, prefix)
- else
- display_flow_item(item)
- end
- end
- print "</#{list_type}>"
- end
- def display_verbatim_flow_item(item, prefix=@indent)
- print("<pre>")
- puts item.body
- puts("</pre>")
- end
- private
- ATTR_MAP = {
- BOLD => "b>",
- ITALIC => "i>",
- CODE => "tt>"
- }
- def update_attributes(current, wanted)
- str = ""
- # first turn off unwanted ones
- off = current & ~wanted
- for quality in [ BOLD, ITALIC, CODE]
- if (off & quality) > 0
- str << "</" + ATTR_MAP[quality]
- end
- end
- # now turn on wanted
- for quality in [ BOLD, ITALIC, CODE]
- unless (wanted & quality).zero?
- str << "<" << ATTR_MAP[quality]
- end
- end
- print str
- end
- def tag(code)
- print("<#{code}>")
- print(yield)
- print("</#{code}>")
- end
- def escape(str)
- str.
- gsub(/&/n, '&').
- gsub(/\"/n, '"').
- gsub(/>/n, '>').
- gsub(/</n, '<')
- end
- end
- ##################################################
-
- # This formatter reduces extra lines for a simpler output.
- # It improves way output looks for tools like IRC bots.
- class SimpleFormatter < TextFormatter
- ######################################################################
- # No extra blank lines
- def blankline
- end
- ######################################################################
- # Display labels only, no lines
- def draw_line(label=nil)
- unless label.nil? then
- bold_print(label)
- puts
- end
- end
- ######################################################################
- # Place heading level indicators inline with heading.
- def display_heading(text, level, indent)
- text = strip_attributes(text)
- case level
- when 1
- puts "= " + text.upcase
- when 2
- puts "-- " + text
- else
- print indent, text, "\n"
- end
- end
- end
- # Finally, fill in the list of known formatters
- class TextFormatter
- FORMATTERS = {
- "ansi" => AnsiFormatter,
- "bs" => OverstrikeFormatter,
- "html" => HtmlFormatter,
- "plain" => TextFormatter,
- "simple" => SimpleFormatter,
- }
-
- def TextFormatter.list
- FORMATTERS.keys.sort.join(", ")
- end
- def TextFormatter.for(name)
- FORMATTERS[name.downcase]
- end
- end
- end