From 46580b51477355fece514573c88cb67030f4a502 Mon Sep 17 00:00:00 2001 From: drbrain Date: Thu, 1 Apr 2010 07:45:16 +0000 Subject: Import RDoc 2.5 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27147 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rdoc/ri/formatter.rb | 615 +---------------------------------------------- 1 file changed, 2 insertions(+), 613 deletions(-) (limited to 'lib/rdoc/ri/formatter.rb') diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb index 258907d141..84d37a9d31 100644 --- a/lib/rdoc/ri/formatter.rb +++ b/lib/rdoc/ri/formatter.rb @@ -1,616 +1,5 @@ -require 'rdoc/ri' -require 'rdoc/markup' - -class RDoc::RI::Formatter - - attr_writer :indent - attr_accessor :output - - FORMATTERS = { } - - def self.for(name) - FORMATTERS[name.downcase] - end - - def self.list - FORMATTERS.keys.sort.join ", " - end - - def initialize(output, width, indent) - @output = output - @width = width - @indent = indent - @original_indent = indent.dup - end - - def draw_line(label=nil) - len = @width - len -= (label.size + 1) if label - - if len > 0 then - @output.print '-' * len - if label - @output.print ' ' - bold_print label - end - - @output.puts - else - @output.print '-' * @width - @output.puts - - @output.puts label - end - end - - def indent - return @indent unless block_given? - - begin - indent = @indent.dup - @indent += @original_indent - yield - ensure - @indent = indent - end - 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? - @output.puts(prefix + res.join("\n" + next_prefix)) - end - - def blankline - @output.puts - end - - ## - # Called when we want to ensure a new '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) - @output.print txt - end - - def raw_print_line(txt) - @output.print txt - end - - ## - # Convert HTML entities back to ASCII - - def conv_html(txt) - txt = txt.gsub(/>/, '>') - txt.gsub!(/</, '<') - txt.gsub!(/"/, '"') - txt.gsub!(/&/, '&') - txt - end - - ## - # Convert markup into display form - - def conv_markup(txt) - txt = txt.gsub(%r{(.*?)}, '+\1+') - txt.gsub!(%r{(.*?)}, '+\1+') - txt.gsub!(%r{(.*?)}, '*\1*') - txt.gsub!(%r{(.*?)}, '_\1_') - txt - end - - def display_list(list) - case list.type - when :BULLET - prefixer = proc { |ignored| @indent + "* " } - - when :NUMBER, :UPPERALPHA, :LOWERALPHA then - start = case list.type - when :NUMBER then 1 - when :UPPERALPHA then 'A' - when :LOWERALPHA then 'a' - end - - prefixer = proc do |ignored| - res = @indent + "#{start}.".ljust(4) - start = start.succ - res - end - - when :LABELED, :NOTE then - longest = 0 - - list.contents.each do |item| - if RDoc::Markup::Flow::LI === item and item.label.length > longest then - longest = item.label.length - end - end - - longest += 1 - - prefixer = proc { |li| @indent + li.label.ljust(longest) } - - else - raise ArgumentError, "unknown list type #{list.type}" - end - - list.contents.each do |item| - if RDoc::Markup::Flow::LI === item then - 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 RDoc::Markup::Flow::P, RDoc::Markup::Flow::LI - wrap(conv_html(item.body), prefix) - blankline - - when RDoc::Markup::Flow::LIST - display_list(item) - - when RDoc::Markup::Flow::VERB - display_verbatim_flow_item(item, @indent) - - when RDoc::Markup::Flow::H - display_heading(conv_html(item.text), item.level, @indent) - - when RDoc::Markup::Flow::RULE - draw_line - - else - raise RDoc::Error, "Unknown flow element: #{item.class}" - end - end - - def display_verbatim_flow_item(item, prefix=@indent) - item.body.split(/\n/).each do |line| - @output.print @indent, conv_html(line), "\n" - end - blankline - end - - def display_heading(text, level, indent) - text = strip_attributes text - - case level - when 1 then - ul = "=" * text.length - @output.puts - @output.puts text.upcase - @output.puts ul - - when 2 then - ul = "-" * text.length - @output.puts - @output.puts text - @output.puts ul - else - @output.print indent, text, "\n" - end - - @output.puts - end - - def display_flow(flow) - flow.each do |f| - display_flow_item(f) - end - end - - def strip_attributes(text) - text.gsub(/(<\/?(?:b|code|em|i|tt)>)/, '') - 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 RDoc::RI::AttributeFormatter < RDoc::RI::Formatter - - BOLD = 1 - ITALIC = 2 - CODE = 4 - - ATTR_MAP = { - "b" => BOLD, - "code" => CODE, - "em" => ITALIC, - "i" => ITALIC, - "tt" => CODE - } - - AttrChar = Struct.new :char, :attr - - 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 ... 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 - - def write_attribute_text(prefix, line) - @output.print prefix - line.each do |achar| - @output.print achar.char - end - @output.puts - end - - def bold_print(txt) - @output.print txt - end - - private - - def add_attributes_to(txt) - tokens = txt.split(%r{()}) - text = AttributeString.new - attributes = 0 - tokens.each do |tok| - case tok - when %r{^$} 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 RDoc::RI::OverstrikeFormatter < RDoc::RI::AttributeFormatter - - BS = "\C-h" - - def write_attribute_text(prefix, line) - @output.print prefix - - line.each do |achar| - attr = achar.attr - @output.print "_", BS if (attr & (ITALIC + CODE)) != 0 - @output.print achar.char, BS if (attr & BOLD) != 0 - @output.print achar.char - end - - @output.puts - end - - ## - # Draw a string in bold - - def bold_print(text) - text.split(//).each do |ch| - @output.print ch, BS, ch - end - end - -end - -## -# This formatter uses ANSI escape sequences to colorize stuff works with -# pagers such as man and less. - -class RDoc::RI::AnsiFormatter < RDoc::RI::AttributeFormatter - - def initialize(*args) - super - @output.print "\033[0m" - end - - def write_attribute_text(prefix, line) - @output.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 - @output.print achar.char - end - update_attributes(0) unless curr_attr.zero? - @output.puts - end - - def bold_print(txt) - @output.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] - @output.print indent - @output.print heading[0] - @output.print strip_attributes(text) - @output.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 - @output.print str, "m" - end - -end - ## -# This formatter uses HTML. - -class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter - - 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 - @output.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 - @output.puts("
") - end - - def bold_print(txt) - tag("b") { txt } - end - - def blankline() - @output.puts("

") - end - - def break_to_newline - @output.puts("
") - end - - def display_heading(text, level, indent) - level = 4 if level > 4 - tag("h#{level}") { text } - @output.puts - end - - def display_list(list) - case list.type - when :BULLET then - list_type = "ul" - prefixer = proc { |ignored| "

  • " } - - when :NUMBER, :UPPERALPHA, :LOWERALPHA then - list_type = "ol" - prefixer = proc { |ignored| "
  • " } - - when :LABELED then - list_type = "dl" - prefixer = proc do |li| - "
    " + escape(li.label) + "
    " - end - - when :NOTE then - list_type = "table" - prefixer = proc do |li| - %{#{li.label.gsub(/ /, ' ')}} - end - else - fail "unknown list type" - end - - @output.print "<#{list_type}>" - list.contents.each do |item| - if item.kind_of? RDoc::Markup::Flow::LI - prefix = prefixer.call(item) - @output.print prefix - display_flow_item(item, prefix) - else - display_flow_item(item) - end - end - @output.print "" - end - - def display_verbatim_flow_item(item, prefix=@indent) - @output.print("
    ")
    -    item.body.split(/\n/).each do |line|
    -      @output.puts conv_html(line)
    -    end
    -    @output.puts("
    ") - 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 << "") - @output.print(yield) - @output.print("") - end - - def escape(str) - str = str.gsub(/&/n, '&') - str.gsub!(/\"/n, '"') - str.gsub!(/>/n, '>') - str.gsub!(/