eaiovnaovbqoebvqoeavibavo verbatim.rb000064400000002405147635633570006726 0ustar00## # A section of verbatim text class RDoc::Markup::Verbatim < RDoc::Markup::Raw ## # Format of this verbatim section attr_accessor :format def initialize *parts # :nodoc: super @format = nil end def == other # :nodoc: super and @format == other.format end ## # Calls #accept_verbatim on +visitor+ def accept visitor visitor.accept_verbatim self end ## # Collapses 3+ newlines into two newlines def normalize parts = [] newlines = 0 @parts.each do |part| case part when /^\s*\n/ then newlines += 1 parts << part if newlines == 1 else newlines = 0 parts << part end end parts.pop if parts.last =~ /\A\r?\n\z/ @parts = parts end def pretty_print q # :nodoc: self.class.name =~ /.*::(\w{1,4})/i q.group 2, "[#{$1.downcase}: ", ']' do if @format then q.text "format: #{@format}" q.breakable end q.seplist @parts do |part| q.pp part end end end ## # Is this verbatim section ruby code? def ruby? @format ||= nil # TODO for older ri data, switch the tree to marshal_dump @format == :ruby end ## # The text of the section def text @parts.join end end rule.rb000064400000000435147635633570006065 0ustar00## # A horizontal rule with a weight class RDoc::Markup::Rule < Struct.new :weight ## # Calls #accept_rule on +visitor+ def accept visitor visitor.accept_rule self end def pretty_print q # :nodoc: q.group 2, '[rule:', ']' do q.pp weight end end end paragraph.rb000064400000000717147635633570007066 0ustar00## # A Paragraph of text class RDoc::Markup::Paragraph < RDoc::Markup::Raw ## # Calls #accept_paragraph on +visitor+ def accept visitor visitor.accept_paragraph self end ## # Joins the raw paragraph text and converts inline HardBreaks to the # +hard_break+ text. def text hard_break = '' @parts.map do |part| if RDoc::Markup::HardBreak === part then hard_break else part end end.join end end hard_break.rb000064400000000640147635633570007176 0ustar00## # A hard-break in the middle of a paragraph. class RDoc::Markup::HardBreak @instance = new ## # RDoc::Markup::HardBreak is a singleton def self.new @instance end ## # Calls #accept_hard_break on +visitor+ def accept visitor visitor.accept_hard_break self end def == other # :nodoc: self.class === other end def pretty_print q # :nodoc: q.text "[break]" end end raw.rb000064400000001712147635633570005706 0ustar00## # A section of text that is added to the output document as-is class RDoc::Markup::Raw ## # The component parts of the list attr_reader :parts ## # Creates a new Raw containing +parts+ def initialize *parts @parts = [] @parts.concat parts end ## # Appends +text+ def << text @parts << text end def == other # :nodoc: self.class == other.class and @parts == other.parts end ## # Calls #accept_raw+ on +visitor+ def accept visitor visitor.accept_raw self end ## # Appends +other+'s parts def merge other @parts.concat other.parts end def pretty_print q # :nodoc: self.class.name =~ /.*::(\w{1,4})/i q.group 2, "[#{$1.downcase}: ", ']' do q.seplist @parts do |part| q.pp part end end end ## # Appends +texts+ onto this Paragraph def push *texts self.parts.concat texts end ## # The raw text def text @parts.join ' ' end end to_label.rb000064400000003422147635633570006676 0ustar00require 'cgi' ## # Creates HTML-safe labels suitable for use in id attributes. Tidylinks are # converted to their link part and cross-reference links have the suppression # marks removed (\\SomeClass is converted to SomeClass). class RDoc::Markup::ToLabel < RDoc::Markup::Formatter attr_reader :res # :nodoc: ## # Creates a new formatter that will output HTML-safe labels def initialize markup = nil super nil, markup @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK) add_tag :BOLD, '', '' add_tag :TT, '', '' add_tag :EM, '', '' @res = [] end ## # Converts +text+ to an HTML-safe label def convert text label = convert_flow @am.flow text CGI.escape label end ## # Converts the CROSSREF +special+ to plain text, removing the suppression # marker, if any def handle_special_CROSSREF special text = special.text text.sub(/^\\/, '') end ## # Converts the TIDYLINK +special+ to just the text part def handle_special_TIDYLINK special text = special.text return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/ $1 end alias accept_blank_line ignore alias accept_block_quote ignore alias accept_heading ignore alias accept_list_end ignore alias accept_list_item_end ignore alias accept_list_item_start ignore alias accept_list_start ignore alias accept_paragraph ignore alias accept_raw ignore alias accept_rule ignore alias accept_verbatim ignore alias end_accepting ignore alias handle_special_HARD_BREAK ignore alias start_accepting ignore end to_tt_only.rb000064400000004361147635633570007312 0ustar00## # Extracts sections of text enclosed in plus, tt or code. Used to discover # undocumented parameters. class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter ## # Stack of list types attr_reader :list_type ## # Output accumulator attr_reader :res ## # Creates a new tt-only formatter. def initialize markup = nil super nil, markup add_tag :TT, nil, nil end ## # Adds tts from +block_quote+ to the output def accept_block_quote block_quote tt_sections block_quote.text end ## # Pops the list type for +list+ from #list_type def accept_list_end list @list_type.pop end ## # Pushes the list type for +list+ onto #list_type def accept_list_start list @list_type << list.type end ## # Prepares the visitor for consuming +list_item+ def accept_list_item_start list_item case @list_type.last when :NOTE, :LABEL then Array(list_item.label).map do |label| tt_sections label end.flatten end end ## # Adds +paragraph+ to the output def accept_paragraph paragraph tt_sections(paragraph.text) end ## # Does nothing to +markup_item+ because it doesn't have any user-built # content def do_nothing markup_item end alias accept_blank_line do_nothing # :nodoc: alias accept_heading do_nothing # :nodoc: alias accept_list_item_end do_nothing # :nodoc: alias accept_raw do_nothing # :nodoc: alias accept_rule do_nothing # :nodoc: alias accept_verbatim do_nothing # :nodoc: ## # Extracts tt sections from +text+ def tt_sections text flow = @am.flow text.dup flow.each do |item| case item when String then @res << item if in_tt? when RDoc::Markup::AttrChanger then off_tags res, item on_tags res, item when RDoc::Markup::Special then @res << convert_special(item) if in_tt? # TODO can this happen? else raise "Unknown flow element: #{item.inspect}" end end res end ## # Returns an Array of items that were wrapped in plus, tt or code. def end_accepting @res.compact end ## # Prepares the visitor for gathering tt sections def start_accepting @res = [] @list_type = [] end end to_html.rb000064400000020160147635633570006561 0ustar00require 'cgi' ## # Outputs RDoc markup as HTML. class RDoc::Markup::ToHtml < RDoc::Markup::Formatter include RDoc::Text # :section: Utilities ## # Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags LIST_TYPE_TO_HTML = { :BULLET => [''], :LABEL => ['
', '
'], :LALPHA => ['
    ', '
'], :NOTE => ['
', '
'], :NUMBER => ['
    ', '
'], :UALPHA => ['
    ', '
'], } attr_reader :res # :nodoc: attr_reader :in_list_entry # :nodoc: attr_reader :list # :nodoc: ## # The RDoc::CodeObject HTML is being generated for. This is used to # generate namespaced URI fragments attr_accessor :code_object ## # Path to this document for relative links attr_accessor :from_path # :section: ## # Creates a new formatter that will output HTML def initialize options, markup = nil super @code_object = nil @from_path = '' @in_list_entry = nil @list = nil @th = nil @hard_break = "
\n" # external links @markup.add_special(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/, :HYPERLINK) add_special_RDOCLINK add_special_TIDYLINK init_tags end # :section: Special Handling # # These methods handle special markup added by RDoc::Markup#add_special. ## # +special+ is a
def handle_special_HARD_BREAK special '
' end ## # +special+ is a potential link. The following schemes are handled: # # mailto::: # Inserted as-is. # http::: # Links are checked to see if they reference an image. If so, that image # gets inserted using an tag. Otherwise a conventional # is used. # link::: # Reference to a local file relative to the output directory. def handle_special_HYPERLINK(special) url = special.text gen_url url, url end ## # +special+ is an rdoc-schemed link that will be converted into a hyperlink. # # For the +rdoc-ref+ scheme the named reference will be returned without # creating a link. # # For the +rdoc-label+ scheme the footnote and label prefixes are stripped # when creating a link. All other contents will be linked verbatim. def handle_special_RDOCLINK special url = special.text case url when /\Ardoc-ref:/ $' when /\Ardoc-label:/ text = $' text = case text when /\Alabel-/ then $' when /\Afootmark-/ then "^#{$'}" when /\Afoottext-/ then "*#{$'}" else text end gen_url url, text else url =~ /\Ardoc-[a-z]+:/ $' end end ## # This +special+ is a link where the label is different from the URL # label[url] or {long label}[url] def handle_special_TIDYLINK(special) text = special.text return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/ label = $1 url = $2 gen_url url, label end # :section: Visitor # # These methods implement the HTML visitor. ## # Prepares the visitor for HTML generation def start_accepting @res = [] @in_list_entry = [] @list = [] end ## # Returns the generated output def end_accepting @res.join end ## # Adds +block_quote+ to the output def accept_block_quote block_quote @res << "\n
" block_quote.parts.each do |part| part.accept self end @res << "
\n" end ## # Adds +paragraph+ to the output def accept_paragraph paragraph @res << "\n

" text = paragraph.text @hard_break @res << wrap(to_html(text)) @res << "

\n" end ## # Adds +verbatim+ to the output def accept_verbatim verbatim text = verbatim.text.rstrip @res << if verbatim.ruby? or parseable? text then begin tokens = RDoc::RubyLex.tokenize text, @options html = RDoc::TokenStream.to_html tokens "\n
#{html}
\n" rescue RDoc::RubyLex::Error "\n
#{CGI.escapeHTML text}
\n" end else "\n
#{CGI.escapeHTML text}
\n" end end ## # Adds +rule+ to the output def accept_rule(rule) size = rule.weight size = 10 if size > 10 @res << "
\n" end ## # Prepares the visitor for consuming +list+ def accept_list_start(list) @list << list.type @res << html_list_name(list.type, true) @in_list_entry.push false end ## # Finishes consumption of +list+ def accept_list_end(list) @list.pop if tag = @in_list_entry.pop @res << tag end @res << html_list_name(list.type, false) << "\n" end ## # Prepares the visitor for consuming +list_item+ def accept_list_item_start(list_item) if tag = @in_list_entry.last @res << tag end @res << list_item_start(list_item, @list.last) end ## # Finishes consumption of +list_item+ def accept_list_item_end(list_item) @in_list_entry[-1] = list_end_for(@list.last) end ## # Adds +blank_line+ to the output def accept_blank_line(blank_line) # @res << annotate("

") << "\n" end ## # Adds +heading+ to the output. The headings greater than 6 are trimmed to # level 6. def accept_heading heading level = [6, heading.level].min label = heading.aref label = [@code_object.aref, label].compact.join '-' if @code_object and @code_object.respond_to? :aref @res << "\n" @res << to_html(heading.text) unless @options.pipe then @res << "" @res << " " end @res << "\n" end ## # Adds +raw+ to the output def accept_raw raw @res << raw.parts.join("\n") end # :section: Utilities ## # CGI-escapes +text+ def convert_string(text) CGI.escapeHTML text end ## # Generate a link to +url+ with content +text+. Handles the special cases # for img: and link: described under handle_special_HYPERLINK def gen_url url, text scheme, url, id = parse_url url if %w[http https link].include?(scheme) and url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then "" else "#{text.sub(%r{^#{scheme}:/*}i, '')}" end end ## # Determines the HTML list element for +list_type+ and +open_tag+ def html_list_name(list_type, open_tag) tags = LIST_TYPE_TO_HTML[list_type] raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags tags[open_tag ? 0 : 1] end ## # Maps attributes to HTML tags def init_tags add_tag :BOLD, "", "" add_tag :TT, "", "" add_tag :EM, "", "" end ## # Returns the HTML tag for +list_type+, possible using a label from # +list_item+ def list_item_start(list_item, list_type) case list_type when :BULLET, :LALPHA, :NUMBER, :UALPHA then "

  • " when :LABEL, :NOTE then Array(list_item.label).map do |label| "
    #{to_html label}\n" end.join << "
    " else raise RDoc::Error, "Invalid list type: #{list_type.inspect}" end end ## # Returns the HTML end-tag for +list_type+ def list_end_for(list_type) case list_type when :BULLET, :LALPHA, :NUMBER, :UALPHA then "
  • " when :LABEL, :NOTE then "" else raise RDoc::Error, "Invalid list type: #{list_type.inspect}" end end ## # Returns true if Ripper is available it can create a sexp from +text+ def parseable? text text =~ /\b(def|class|module|require) |=>|\{\s?\||do \|/ and text !~ /<%|%>/ end ## # Converts +item+ to HTML using RDoc::Text#to_html def to_html item super convert_flow @am.flow item end end document.rb000064400000006172147635633570006740 0ustar00## # A Document containing lists, headings, paragraphs, etc. class RDoc::Markup::Document include Enumerable ## # The file this document was created from. See also # RDoc::ClassModule#add_comment attr_reader :file ## # If a heading is below the given level it will be omitted from the # table_of_contents attr_accessor :omit_headings_below ## # The parts of the Document attr_reader :parts ## # Creates a new Document with +parts+ def initialize *parts @parts = [] @parts.concat parts @file = nil @omit_headings_from_table_of_contents_below = nil end ## # Appends +part+ to the document def << part case part when RDoc::Markup::Document then unless part.empty? then parts.concat part.parts parts << RDoc::Markup::BlankLine.new end when String then raise ArgumentError, "expected RDoc::Markup::Document and friends, got String" unless part.empty? else parts << part end end def == other # :nodoc: self.class == other.class and @file == other.file and @parts == other.parts end ## # Runs this document and all its #items through +visitor+ def accept visitor visitor.start_accepting visitor.accept_document self visitor.end_accepting end ## # Concatenates the given +parts+ onto the document def concat parts self.parts.concat parts end ## # Enumerator for the parts of this document def each &block @parts.each(&block) end ## # Does this document have no parts? def empty? @parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?) end ## # The file this Document was created from. def file= location @file = case location when RDoc::TopLevel then location.relative_name else location end end ## # When this is a collection of documents (#file is not set and this document # contains only other documents as its direct children) #merge replaces # documents in this class with documents from +other+ when the file matches # and adds documents from +other+ when the files do not. # # The information in +other+ is preferred over the receiver def merge other if empty? then @parts = other.parts return self end other.parts.each do |other_part| self.parts.delete_if do |self_part| self_part.file and self_part.file == other_part.file end self.parts << other_part end self end ## # Does this Document contain other Documents? def merged? RDoc::Markup::Document === @parts.first end def pretty_print q # :nodoc: start = @file ? "[doc (#{@file}): " : '[doc: ' q.group 2, start, ']' do q.seplist @parts do |part| q.pp part end end end ## # Appends +parts+ to the document def push *parts self.parts.concat parts end ## # Returns an Array of headings in the document. # # Require 'rdoc/markup/formatter' before calling this method. def table_of_contents accept RDoc::Markup::ToTableOfContents.to_toc end end to_html_crossref.rb000064400000010256147635633570010474 0ustar00## # Subclass of the RDoc::Markup::ToHtml class that supports looking up method # names, classes, etc to create links. RDoc::CrossReference is used to # generate those links based on the current context. class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml # :stopdoc: ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP CLASS_REGEXP_STR = RDoc::CrossReference::CLASS_REGEXP_STR CROSSREF_REGEXP = RDoc::CrossReference::CROSSREF_REGEXP METHOD_REGEXP_STR = RDoc::CrossReference::METHOD_REGEXP_STR # :startdoc: ## # RDoc::CodeObject for generating references attr_accessor :context ## # Should we show '#' characters on method references? attr_accessor :show_hash ## # Creates a new crossref resolver that generates links relative to +context+ # which lives at +from_path+ in the generated files. '#' characters on # references are removed unless +show_hash+ is true. Only method names # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true. def initialize(options, from_path, context, markup = nil) raise ArgumentError, 'from_path cannot be nil' if from_path.nil? super options, markup @context = context @from_path = from_path @hyperlink_all = @options.hyperlink_all @show_hash = @options.show_hash crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP @markup.add_special crossref_re, :CROSSREF @cross_reference = RDoc::CrossReference.new @context end ## # Creates a link to the reference +name+ if the name exists. If +text+ is # given it is used as the link text, otherwise +name+ is used. def cross_reference name, text = nil lookup = name name = name[1..-1] unless @show_hash if name[0, 1] == '#' name = "#{CGI.unescape $'} at #{$1}" if name =~ /(.*[^#:])@/ text = name unless text link lookup, text end ## # We're invoked when any text matches the CROSSREF pattern. If we find the # corresponding reference, generate a link. If the name we're looking for # contains no punctuation, we look for it up the module/class chain. For # example, ToHtml is found, even without the RDoc::Markup:: prefix, # because we look for it in module Markup first. def handle_special_CROSSREF(special) name = special.text return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails unless @hyperlink_all then # This ensures that words entirely consisting of lowercase letters will # not have cross-references generated (to suppress lots of erroneous # cross-references to "new" in text, for instance) return name if name =~ /\A[a-z]*\z/ end cross_reference name end ## # Handles rdoc-ref: scheme links and allows RDoc::Markup::ToHtml to # handle other schemes. def handle_special_HYPERLINK special return cross_reference $' if special.text =~ /\Ardoc-ref:/ super end ## # +special+ is an rdoc-schemed link that will be converted into a hyperlink. # For the rdoc-ref scheme the cross-reference will be looked up and the # given name will be used. # # All other contents are handled by # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_special_RDOCLINK] def handle_special_RDOCLINK special url = special.text case url when /\Ardoc-ref:/ then cross_reference $' else super end end ## # Generates links for rdoc-ref: scheme URLs and allows # RDoc::Markup::ToHtml to handle other schemes. def gen_url url, text return super unless url =~ /\Ardoc-ref:/ cross_reference $', text end ## # Creates an HTML link to +name+ with the given +text+. def link name, text original_name = name if name =~ /(.*[^#:])@/ then name = $1 label = $' end ref = @cross_reference.resolve name, text text = ref.output_name @context if RDoc::MethodAttr === ref and text == original_name case ref when String then ref else path = ref.as_href @from_path if path =~ /#/ then path << "-label-#{label}" else path << "#label-#{label}" end if label "#{text}" end end end inline.rb000064400000000130147635633570006364 0ustar00warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w formatter.rb000064400000012452147635633570007123 0ustar00## # Base class for RDoc markup formatters # # Formatters are a visitor that converts an RDoc::Markup tree (from a comment) # into some kind of output. RDoc ships with formatters for converting back to # rdoc, ANSI text, HTML, a Table of Contents and other formats. # # If you'd like to write your own Formatter use # RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter # use RDoc::Markup::TextFormatterTestCase which provides extra test cases. class RDoc::Markup::Formatter ## # Tag for inline markup containing a +bit+ for the bitmask and the +on+ and # +off+ triggers. InlineTag = Struct.new(:bit, :on, :off) ## # Converts a target url to one that is relative to a given path def self.gen_relative_url path, target from = File.dirname path to, to_file = File.split target from = from.split "/" to = to.split "/" from.delete '.' to.delete '.' while from.size > 0 and to.size > 0 and from[0] == to[0] do from.shift to.shift end from.fill ".." from.concat to from << to_file File.join(*from) end ## # Creates a new Formatter def initialize options, markup = nil @options = options @markup = markup || RDoc::Markup.new @am = @markup.attribute_manager @am.add_special(/
    /, :HARD_BREAK) @attributes = @am.attributes @attr_tags = [] @in_tt = 0 @tt_bit = @attributes.bitmap_for :TT @hard_break = '' @from_path = '.' end ## # Adds +document+ to the output def accept_document document document.parts.each do |item| case item when RDoc::Markup::Document then # HACK accept_document item else item.accept self end end end ## # Adds a special for links of the form rdoc-...: def add_special_RDOCLINK @markup.add_special(/rdoc-[a-z]+:\S+/, :RDOCLINK) end ## # Adds a special for links of the form {}[] and [] def add_special_TIDYLINK @markup.add_special(/(?: \{.*?\} | # multi-word label \b[^\s{}]+? # single-word label ) \[\S+?\] # link target /x, :TIDYLINK) end ## # Add a new set of tags for an attribute. We allow separate start and end # tags for flexibility def add_tag(name, start, stop) attr = @attributes.bitmap_for name @attr_tags << InlineTag.new(attr, start, stop) end ## # Allows +tag+ to be decorated with additional information. def annotate(tag) tag end ## # Marks up +content+ def convert content @markup.convert content, self end ## # Converts flow items +flow+ def convert_flow(flow) res = [] flow.each do |item| case item when String then res << convert_string(item) when RDoc::Markup::AttrChanger then off_tags res, item on_tags res, item when RDoc::Markup::Special then res << convert_special(item) else raise "Unknown flow element: #{item.inspect}" end end res.join end ## # Converts added specials. See RDoc::Markup#add_special def convert_special special return special.text if in_tt? handled = false @attributes.each_name_of special.type do |name| method_name = "handle_special_#{name}" if respond_to? method_name then special.text = send method_name, special handled = true end end unless handled then special_name = @attributes.as_string special.type raise RDoc::Error, "Unhandled special #{special_name}: #{special}" end special.text end ## # Converts a string to be fancier if desired def convert_string string string end ## # Use ignore in your subclass to ignore the content of a node. # # ## # # We don't support raw nodes in ToNoRaw # # alias accept_raw ignore def ignore *node end ## # Are we currently inside tt tags? def in_tt? @in_tt > 0 end ## # Turns on tags for +item+ on +res+ def on_tags res, item attr_mask = item.turn_on return if attr_mask.zero? @attr_tags.each do |tag| if attr_mask & tag.bit != 0 then res << annotate(tag.on) @in_tt += 1 if tt? tag end end end ## # Turns off tags for +item+ on +res+ def off_tags res, item attr_mask = item.turn_off return if attr_mask.zero? @attr_tags.reverse_each do |tag| if attr_mask & tag.bit != 0 then @in_tt -= 1 if tt? tag res << annotate(tag.off) end end end ## # Extracts and a scheme, url and an anchor id from +url+ and returns them. def parse_url url case url when /^rdoc-label:([^:]*)(?::(.*))?/ then scheme = 'link' path = "##{$1}" id = " id=\"#{$2}\"" if $2 when /([A-Za-z]+):(.*)/ then scheme = $1.downcase path = $2 when /^#/ then else scheme = 'http' path = url url = "http://#{url}" end if scheme == 'link' then url = if path[0, 1] == '#' then # is this meaningful? path else self.class.gen_relative_url @from_path, path end end [scheme, url, id] end ## # Is +tag+ a tt tag? def tt? tag tag.bit == @tt_bit end end to_rdoc.rb000064400000014171147635633570006551 0ustar00## # Outputs RDoc markup as RDoc markup! (mostly) class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter ## # Current indent amount for output in characters attr_accessor :indent ## # Output width in characters attr_accessor :width ## # Stack of current list indexes for alphabetic and numeric lists attr_reader :list_index ## # Stack of list types attr_reader :list_type ## # Stack of list widths for indentation attr_reader :list_width ## # Prefix for the next list item. See #use_prefix attr_reader :prefix ## # Output accumulator attr_reader :res ## # Creates a new formatter that will output (mostly) \RDoc markup def initialize markup = nil super nil, markup @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF) @width = 78 init_tags @headings = {} @headings.default = [] @headings[1] = ['= ', ''] @headings[2] = ['== ', ''] @headings[3] = ['=== ', ''] @headings[4] = ['==== ', ''] @headings[5] = ['===== ', ''] @headings[6] = ['====== ', ''] @hard_break = "\n" end ## # Maps attributes to HTML sequences def init_tags add_tag :BOLD, "", "" add_tag :TT, "", "" add_tag :EM, "", "" end ## # Adds +blank_line+ to the output def accept_blank_line blank_line @res << "\n" end ## # Adds +paragraph+ to the output def accept_block_quote block_quote @indent += 2 block_quote.parts.each do |part| @prefix = '> ' part.accept self end @indent -= 2 end ## # Adds +heading+ to the output def accept_heading heading use_prefix or @res << ' ' * @indent @res << @headings[heading.level][0] @res << attributes(heading.text) @res << @headings[heading.level][1] @res << "\n" end ## # Finishes consumption of +list+ def accept_list_end list @list_index.pop @list_type.pop @list_width.pop end ## # Finishes consumption of +list_item+ def accept_list_item_end list_item width = case @list_type.last when :BULLET then 2 when :NOTE, :LABEL then if @prefix then @res << @prefix.strip @prefix = nil end @res << "\n" 2 else bullet = @list_index.last.to_s @list_index[-1] = @list_index.last.succ bullet.length + 2 end @indent -= width end ## # Prepares the visitor for consuming +list_item+ def accept_list_item_start list_item type = @list_type.last case type when :NOTE, :LABEL then bullets = Array(list_item.label).map do |label| attributes(label).strip end.join "\n" bullets << ":\n" unless bullets.empty? @prefix = ' ' * @indent @indent += 2 @prefix << bullets + (' ' * @indent) else bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.' @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1) width = bullet.length + 1 @indent += width end end ## # Prepares the visitor for consuming +list+ def accept_list_start list case list.type when :BULLET then @list_index << nil @list_width << 1 when :LABEL, :NOTE then @list_index << nil @list_width << 2 when :LALPHA then @list_index << 'a' @list_width << list.items.length.to_s.length when :NUMBER then @list_index << 1 @list_width << list.items.length.to_s.length when :UALPHA then @list_index << 'A' @list_width << list.items.length.to_s.length else raise RDoc::Error, "invalid list type #{list.type}" end @list_type << list.type end ## # Adds +paragraph+ to the output def accept_paragraph paragraph text = paragraph.text @hard_break wrap attributes text end ## # Adds +paragraph+ to the output def accept_indented_paragraph paragraph @indent += paragraph.indent text = paragraph.text @hard_break wrap attributes text @indent -= paragraph.indent end ## # Adds +raw+ to the output def accept_raw raw @res << raw.parts.join("\n") end ## # Adds +rule+ to the output def accept_rule rule use_prefix or @res << ' ' * @indent @res << '-' * (@width - @indent) @res << "\n" end ## # Outputs +verbatim+ indented 2 columns def accept_verbatim verbatim indent = ' ' * (@indent + 2) verbatim.parts.each do |part| @res << indent unless part == "\n" @res << part end @res << "\n" unless @res =~ /\n\z/ end ## # Applies attribute-specific markup to +text+ using RDoc::AttributeManager def attributes text flow = @am.flow text.dup convert_flow flow end ## # Returns the generated output def end_accepting @res.join end ## # Removes preceding \\ from the suppressed crossref +special+ def handle_special_SUPPRESSED_CROSSREF special text = special.text text = text.sub('\\', '') unless in_tt? text end ## # Adds a newline to the output def handle_special_HARD_BREAK special "\n" end ## # Prepares the visitor for text generation def start_accepting @res = [""] @indent = 0 @prefix = nil @list_index = [] @list_type = [] @list_width = [] end ## # Adds the stored #prefix to the output and clears it. Lists generate a # prefix for later consumption. def use_prefix prefix, @prefix = @prefix, nil @res << prefix if prefix prefix end ## # Wraps +text+ to #width def wrap text return unless text && !text.empty? text_len = @width - @indent text_len = 20 if text_len < 20 re = /^(.{0,#{text_len}})[ \n]/ next_prefix = ' ' * @indent prefix = @prefix || next_prefix @prefix = nil @res << prefix while text.length > text_len if text =~ re then @res << $1 text.slice!(0, $&.length) else @res << text.slice!(0, text_len) end @res << "\n" << next_prefix end if text.empty? then @res.pop @res.pop else @res << text @res << "\n" end end end to_markdown.rb000064400000007061147635633570007444 0ustar00# :markup: markdown ## # Outputs parsed markup as Markdown class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc ## # Creates a new formatter that will output Markdown format text def initialize markup = nil super @headings[1] = ['# ', ''] @headings[2] = ['## ', ''] @headings[3] = ['### ', ''] @headings[4] = ['#### ', ''] @headings[5] = ['##### ', ''] @headings[6] = ['###### ', ''] add_special_RDOCLINK add_special_TIDYLINK @hard_break = " \n" end ## # Maps attributes to HTML sequences def init_tags add_tag :BOLD, '**', '**' add_tag :EM, '*', '*' add_tag :TT, '`', '`' end ## # Adds a newline to the output def handle_special_HARD_BREAK special " \n" end ## # Finishes consumption of `list` def accept_list_end list @res << "\n" super end ## # Finishes consumption of `list_item` def accept_list_item_end list_item width = case @list_type.last when :BULLET then 4 when :NOTE, :LABEL then use_prefix 4 else @list_index[-1] = @list_index.last.succ 4 end @indent -= width end ## # Prepares the visitor for consuming `list_item` def accept_list_item_start list_item type = @list_type.last case type when :NOTE, :LABEL then bullets = Array(list_item.label).map do |label| attributes(label).strip end.join "\n" bullets << "\n:" @prefix = ' ' * @indent @indent += 4 @prefix << bullets + (' ' * (@indent - 1)) else bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.' @prefix = (' ' * @indent) + bullet.ljust(4) @indent += 4 end end ## # Prepares the visitor for consuming `list` def accept_list_start list case list.type when :BULLET, :LABEL, :NOTE then @list_index << nil when :LALPHA, :NUMBER, :UALPHA then @list_index << 1 else raise RDoc::Error, "invalid list type #{list.type}" end @list_width << 4 @list_type << list.type end ## # Adds `rule` to the output def accept_rule rule use_prefix or @res << ' ' * @indent @res << '-' * 3 @res << "\n" end ## # Outputs `verbatim` indented 4 columns def accept_verbatim verbatim indent = ' ' * (@indent + 4) verbatim.parts.each do |part| @res << indent unless part == "\n" @res << part end @res << "\n" unless @res =~ /\n\z/ end ## # Creates a Markdown-style URL from +url+ with +text+. def gen_url url, text scheme, url, = parse_url url "[#{text.sub(%r{^#{scheme}:/*}i, '')}](#{url})" end ## # Handles rdoc- type links for footnotes. def handle_rdoc_link url case url when /\Ardoc-ref:/ then $' when /\Ardoc-label:footmark-(\d+)/ then "[^#{$1}]:" when /\Ardoc-label:foottext-(\d+)/ then "[^#{$1}]" when /\Ardoc-label:label-/ then gen_url url, $' when /\Ardoc-[a-z]+:/ then $' end end ## # Converts the RDoc markup tidylink into a Markdown.style link. def handle_special_TIDYLINK special text = special.text return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/ label = $1 url = $2 if url =~ /^rdoc-label:foot/ then handle_rdoc_link url else gen_url url, label end end ## # Converts the rdoc-...: links into a Markdown.style links. def handle_special_RDOCLINK special handle_rdoc_link special.text end end parser.rb000064400000034034147635633570006414 0ustar00require 'strscan' ## # A recursive-descent parser for RDoc markup. # # The parser tokenizes an input string then parses the tokens into a Document. # Documents can be converted into output formats by writing a visitor like # RDoc::Markup::ToHTML. # # The parser only handles the block-level constructs Paragraph, List, # ListItem, Heading, Verbatim, BlankLine and Rule. Inline markup such as # \+blah\+ is handled separately by RDoc::Markup::AttributeManager. # # To see what markup the Parser implements read RDoc. To see how to use # RDoc markup to format text in your program read RDoc::Markup. class RDoc::Markup::Parser include RDoc::Text ## # List token types LIST_TOKENS = [ :BULLET, :LABEL, :LALPHA, :NOTE, :NUMBER, :UALPHA, ] ## # Parser error subclass class Error < RuntimeError; end ## # Raised when the parser is unable to handle the given markup class ParseError < Error; end ## # Enables display of debugging information attr_accessor :debug ## # Token accessor attr_reader :tokens ## # Parses +str+ into a Document. # # Use RDoc::Markup#parse instead of this method. def self.parse str parser = new parser.tokenize str doc = RDoc::Markup::Document.new parser.parse doc end ## # Returns a token stream for +str+, for testing def self.tokenize str parser = new parser.tokenize str parser.tokens end ## # Creates a new Parser. See also ::parse def initialize @binary_input = nil @current_token = nil @debug = false @have_encoding = Object.const_defined? :Encoding @have_byteslice = ''.respond_to? :byteslice @input = nil @input_encoding = nil @line = 0 @line_pos = 0 @s = nil @tokens = [] end ## # Builds a Heading of +level+ def build_heading level type, text, = get text = case type when :TEXT then skip :NEWLINE text else unget '' end RDoc::Markup::Heading.new level, text end ## # Builds a List flush to +margin+ def build_list margin p :list_start => margin if @debug list = RDoc::Markup::List.new label = nil until @tokens.empty? do type, data, column, = get case type when *LIST_TOKENS then if column < margin || (list.type && list.type != type) then unget break end list.type = type peek_type, _, column, = peek_token case type when :NOTE, :LABEL then label = [] unless label if peek_type == :NEWLINE then # description not on the same line as LABEL/NOTE # skip the trailing newline & any blank lines below while peek_type == :NEWLINE get peek_type, _, column, = peek_token end # we may be: # - at end of stream # - at a column < margin: # [text] # blah blah blah # - at the same column, but with a different type of list item # [text] # * blah blah # - at the same column, with the same type of list item # [one] # [two] # In all cases, we have an empty description. # In the last case only, we continue. if peek_type.nil? || column < margin then empty = true elsif column == margin then case peek_type when type empty = :continue when *LIST_TOKENS empty = true else empty = false end else empty = false end if empty then label << data next if empty == :continue break end end else data = nil end if label then data = label << data label = nil end list_item = RDoc::Markup::ListItem.new data parse list_item, column list << list_item else unget break end end p :list_end => margin if @debug if list.empty? then return nil unless label return nil unless [:LABEL, :NOTE].include? list.type list_item = RDoc::Markup::ListItem.new label, RDoc::Markup::BlankLine.new list << list_item end list end ## # Builds a Paragraph that is flush to +margin+ def build_paragraph margin p :paragraph_start => margin if @debug paragraph = RDoc::Markup::Paragraph.new until @tokens.empty? do type, data, column, = get if type == :TEXT and column == margin then paragraph << data break if peek_token.first == :BREAK data << ' ' if skip :NEWLINE else unget break end end paragraph.parts.last.sub!(/ \z/, '') # cleanup p :paragraph_end => margin if @debug paragraph end ## # Builds a Verbatim that is indented from +margin+. # # The verbatim block is shifted left (the least indented lines start in # column 0). Each part of the verbatim is one line of text, always # terminated by a newline. Blank lines always consist of a single newline # character, and there is never a single newline at the end of the verbatim. def build_verbatim margin p :verbatim_begin => margin if @debug verbatim = RDoc::Markup::Verbatim.new min_indent = nil generate_leading_spaces = true line = '' until @tokens.empty? do type, data, column, = get if type == :NEWLINE then line << data verbatim << line line = '' generate_leading_spaces = true next end if column <= margin unget break end if generate_leading_spaces then indent = column - margin line << ' ' * indent min_indent = indent if min_indent.nil? || indent < min_indent generate_leading_spaces = false end case type when :HEADER then line << '=' * data _, _, peek_column, = peek_token peek_column ||= column + data indent = peek_column - column - data line << ' ' * indent when :RULE then width = 2 + data line << '-' * width _, _, peek_column, = peek_token peek_column ||= column + width indent = peek_column - column - width line << ' ' * indent when :BREAK, :TEXT then line << data else # *LIST_TOKENS list_marker = case type when :BULLET then data when :LABEL then "[#{data}]" when :NOTE then "#{data}::" else # :LALPHA, :NUMBER, :UALPHA "#{data}." end line << list_marker peek_type, _, peek_column = peek_token unless peek_type == :NEWLINE then peek_column ||= column + list_marker.length indent = peek_column - column - list_marker.length line << ' ' * indent end end end verbatim << line << "\n" unless line.empty? verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0 verbatim.normalize p :verbatim_end => margin if @debug verbatim end ## # The character offset for the input string at the given +byte_offset+ def char_pos byte_offset if @have_byteslice then @input.byteslice(0, byte_offset).length elsif @have_encoding then matched = @binary_input[0, byte_offset] matched.force_encoding @input_encoding matched.length else byte_offset end end ## # Pulls the next token from the stream. def get @current_token = @tokens.shift p :get => @current_token if @debug @current_token end ## # Parses the tokens into an array of RDoc::Markup::XXX objects, # and appends them to the passed +parent+ RDoc::Markup::YYY object. # # Exits at the end of the token stream, or when it encounters a token # in a column less than +indent+ (unless it is a NEWLINE). # # Returns +parent+. def parse parent, indent = 0 p :parse_start => indent if @debug until @tokens.empty? do type, data, column, = get case type when :BREAK then parent << RDoc::Markup::BlankLine.new skip :NEWLINE, false next when :NEWLINE then # trailing newlines are skipped below, so this is a blank line parent << RDoc::Markup::BlankLine.new skip :NEWLINE, false next end # indentation change: break or verbatim if column < indent then unget break elsif column > indent then unget parent << build_verbatim(indent) next end # indentation is the same case type when :HEADER then parent << build_heading(data) when :RULE then parent << RDoc::Markup::Rule.new(data) skip :NEWLINE when :TEXT then unget parent << build_paragraph(indent) when *LIST_TOKENS then unget parent << build_list(indent) else type, data, column, line = @current_token raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}" end end p :parse_end => indent if @debug parent end ## # Returns the next token on the stream without modifying the stream def peek_token token = @tokens.first || [] p :peek => token if @debug token end ## # Creates the StringScanner def setup_scanner input @line = 0 @line_pos = 0 @input = input.dup if @have_encoding and not @have_byteslice then @input_encoding = @input.encoding @binary_input = @input.force_encoding Encoding::BINARY end @s = StringScanner.new input end ## # Skips the next token if its type is +token_type+. # # Optionally raises an error if the next token is not of the expected type. def skip token_type, error = true type, = get return unless type # end of stream return @current_token if token_type == type unget raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error end ## # Turns text +input+ into a stream of tokens def tokenize input setup_scanner input until @s.eos? do pos = @s.pos # leading spaces will be reflected by the column of the next token # the only thing we loose are trailing spaces at the end of the file next if @s.scan(/ +/) # note: after BULLET, LABEL, etc., # indent will be the column of the next non-newline token @tokens << case # [CR]LF => :NEWLINE when @s.scan(/\r?\n/) then token = [:NEWLINE, @s.matched, *token_pos(pos)] @line_pos = char_pos @s.pos @line += 1 token # === text => :HEADER then :TEXT when @s.scan(/(=+)(\s*)/) then level = @s[1].length header = [:HEADER, level, *token_pos(pos)] if @s[2] =~ /^\r?\n/ then @s.pos -= @s[2].length header else pos = @s.pos @s.scan(/.*/) @tokens << header [:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)] end # --- (at least 3) and nothing else on the line => :RULE when @s.scan(/(-{3,}) *\r?$/) then [:RULE, @s[1].length - 2, *token_pos(pos)] # * or - followed by white space and text => :BULLET when @s.scan(/([*-]) +(\S)/) then @s.pos -= @s[2].bytesize # unget \S [:BULLET, @s[1], *token_pos(pos)] # A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER when @s.scan(/([a-z]|\d+)\. +(\S)/i) then # FIXME if tab(s), the column will be wrong # either support tabs everywhere by first expanding them to # spaces, or assume that they will have been replaced # before (and provide a check for that at least in debug # mode) list_label = @s[1] @s.pos -= @s[2].bytesize # unget \S list_type = case list_label when /[a-z]/ then :LALPHA when /[A-Z]/ then :UALPHA when /\d/ then :NUMBER else raise ParseError, "BUG token #{list_label}" end [list_type, list_label, *token_pos(pos)] # [text] followed by spaces or end of line => :LABEL when @s.scan(/\[(.*?)\]( +|\r?$)/) then [:LABEL, @s[1], *token_pos(pos)] # text:: followed by spaces or end of line => :NOTE when @s.scan(/(.*?)::( +|\r?$)/) then [:NOTE, @s[1], *token_pos(pos)] # anything else: :TEXT else @s.scan(/(.*?)( )?\r?$/) token = [:TEXT, @s[1], *token_pos(pos)] if @s[2] then @tokens << token [:BREAK, @s[2], *token_pos(pos + @s[1].length)] else token end end end self end ## # Calculates the column (by character) and line of the current token from # +scanner+ based on +byte_offset+. def token_pos byte_offset offset = char_pos byte_offset [offset - @line_pos, @line] end ## # Returns the current token to the token stream def unget token = @current_token p :unget => token if @debug raise Error, 'too many #ungets' if token == @tokens.first @tokens.unshift token if token end end block_quote.rb000064400000000336147635633570007425 0ustar00## # A quoted section which contains markup items. class RDoc::Markup::BlockQuote < RDoc::Markup::Raw ## # Calls #accept_block_quote on +visitor+ def accept visitor visitor.accept_block_quote self end end special.rb000064400000001163147635633570006535 0ustar00## # Hold details of a special sequence class RDoc::Markup::Special ## # Special type attr_reader :type ## # Special text attr_accessor :text ## # Creates a new special sequence of +type+ with +text+ def initialize(type, text) @type, @text = type, text end ## # Specials are equal when the have the same text and type def ==(o) self.text == o.text && self.type == o.type end def inspect # :nodoc: "#" % [ object_id, @type, text.dump] end def to_s # :nodoc: "Special: type=#{type} text=#{text.dump}" end end to_bs.rb000064400000003257147635633570006231 0ustar00## # Outputs RDoc markup with hot backspace action! You will probably need a # pager to use this output format. # # This formatter won't work on 1.8.6 because it lacks String#chars. class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc ## # Returns a new ToBs that is ready for hot backspace action! def initialize markup = nil super @in_b = false @in_em = false end ## # Sets a flag that is picked up by #annotate to do the right thing in # #convert_string def init_tags add_tag :BOLD, '+b', '-b' add_tag :EM, '+_', '-_' add_tag :TT, '' , '' # we need in_tt information maintained end ## # Makes heading text bold. def accept_heading heading use_prefix or @res << ' ' * @indent @res << @headings[heading.level][0] @in_b = true @res << attributes(heading.text) @in_b = false @res << @headings[heading.level][1] @res << "\n" end ## # Turns on or off special handling for +convert_string+ def annotate tag case tag when '+b' then @in_b = true when '-b' then @in_b = false when '+_' then @in_em = true when '-_' then @in_em = false end '' end ## # Calls convert_string on the result of convert_special def convert_special special convert_string super end ## # Adds bold or underline mixed with backspaces def convert_string string return string unless string.respond_to? :chars # your ruby is lame return string unless @in_b or @in_em chars = if @in_b then string.chars.map do |char| "#{char}\b#{char}" end elsif @in_em then string.chars.map do |char| "_\b#{char}" end end chars.join end end pre_process.rb000064400000020217147635633570007442 0ustar00## # Handle common directives that can occur in a block of text: # # \:include: filename # # Directives can be escaped by preceding them with a backslash. # # RDoc plugin authors can register additional directives to be handled by # using RDoc::Markup::PreProcess::register. # # Any directive that is not built-in to RDoc (including those registered via # plugins) will be stored in the metadata hash on the CodeObject the comment # is attached to. See RDoc::Markup@Directives for the list of built-in # directives. class RDoc::Markup::PreProcess ## # An RDoc::Options instance that will be filled in with overrides from # directives attr_accessor :options ## # Adds a post-process handler for directives. The handler will be called # with the result RDoc::Comment (or text String) and the code object for the # comment (if any). def self.post_process &block @post_processors << block end ## # Registered post-processors def self.post_processors @post_processors end ## # Registers +directive+ as one handled by RDoc. If a block is given the # directive will be replaced by the result of the block, otherwise the # directive will be removed from the processed text. # # The block will be called with the directive name and the directive # parameter: # # RDoc::Markup::PreProcess.register 'my-directive' do |directive, param| # # replace text, etc. # end def self.register directive, &block @registered[directive] = block end ## # Registered directives def self.registered @registered end ## # Clears all registered directives and post-processors def self.reset @post_processors = [] @registered = {} end reset ## # Creates a new pre-processor for +input_file_name+ that will look for # included files in +include_path+ def initialize(input_file_name, include_path) @input_file_name = input_file_name @include_path = include_path @options = nil end ## # Look for directives in the given +text+. # # Options that we don't handle are yielded. If the block returns false the # directive is restored to the text. If the block returns nil or no block # was given the directive is handled according to the registered directives. # If a String was returned the directive is replaced with the string. # # If no matching directive was registered the directive is restored to the # text. # # If +code_object+ is given and the directive is unknown then the # directive's parameter is set as metadata on the +code_object+. See # RDoc::CodeObject#metadata for details. def handle text, code_object = nil, &block if RDoc::Comment === text then comment = text text = text.text end encoding = text.encoding if defined?(Encoding) # regexp helper (square brackets for optional) # $1 $2 $3 $4 $5 # [prefix][\]:directive:[spaces][param]newline text.gsub!(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?(\r?\n|$)/) do # skip something like ':toto::' next $& if $4.empty? and $5 and $5[0, 1] == ':' # skip if escaped next "#$1:#$3:#$4#$5\n" unless $2.empty? # This is not in handle_directive because I didn't want to pass another # argument into it if comment and $3 == 'markup' then next "#{$1.strip}\n" unless $5 comment.format = $5.downcase next "#{$1.strip}\n" end handle_directive $1, $3, $5, code_object, encoding, &block end comment = text unless comment self.class.post_processors.each do |handler| handler.call comment, code_object end text end ## # Performs the actions described by +directive+ and its parameter +param+. # # +code_object+ is used for directives that operate on a class or module. # +prefix+ is used to ensure the replacement for handled directives is # correct. +encoding+ is used for the include directive. # # For a list of directives in RDoc see RDoc::Markup. #-- # When 1.8.7 support is ditched prefix can be defaulted to '' def handle_directive prefix, directive, param, code_object = nil, encoding = nil blankline = "#{prefix.strip}\n" directive = directive.downcase case directive when 'arg', 'args' then return blankline unless code_object code_object.params = param blankline when 'category' then if RDoc::Context === code_object then section = code_object.add_section param code_object.temporary_section = section end blankline # ignore category if we're not on an RDoc::Context when 'doc' then return blankline unless code_object code_object.document_self = true code_object.force_documentation = true blankline when 'enddoc' then return blankline unless code_object code_object.done_documenting = true blankline when 'include' then filename = param.split.first include_file filename, prefix, encoding when 'main' then @options.main_page = param if @options.respond_to? :main_page blankline when 'nodoc' then return blankline unless code_object code_object.document_self = nil # notify nodoc code_object.document_children = param !~ /all/i blankline when 'notnew', 'not_new', 'not-new' then return blankline unless RDoc::AnyMethod === code_object code_object.dont_rename_initialize = true blankline when 'startdoc' then return blankline unless code_object code_object.start_doc code_object.force_documentation = true blankline when 'stopdoc' then return blankline unless code_object code_object.stop_doc blankline when 'title' then @options.default_title = param if @options.respond_to? :default_title= blankline when 'yield', 'yields' then return blankline unless code_object # remove parameter &block code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params code_object.block_params = param blankline else result = yield directive, param if block_given? case result when nil then code_object.metadata[directive] = param if code_object if RDoc::Markup::PreProcess.registered.include? directive then handler = RDoc::Markup::PreProcess.registered[directive] result = handler.call directive, param if handler else result = "#{prefix}:#{directive}: #{param}\n" end when false then result = "#{prefix}:#{directive}: #{param}\n" end result end end ## # Handles the :include: _filename_ directive. # # If the first line of the included file starts with '#', and contains # an encoding information in the form 'coding:' or 'coding=', it is # removed. # # If all lines in the included file start with a '#', this leading '#' # is removed before inclusion. The included content is indented like # the :include: directive. #-- # so all content will be verbatim because of the likely space after '#'? # TODO shift left the whole file content in that case # TODO comment stop/start #-- and #++ in included file must be processed here def include_file name, indent, encoding full_name = find_include_file name unless full_name then warn "Couldn't find file to include '#{name}' from #{@input_file_name}" return '' end content = RDoc::Encoding.read_file full_name, encoding, true # strip magic comment content = content.sub(/\A# .*coding[=:].*$/, '').lstrip # strip leading '#'s, but only if all lines start with them if content =~ /^[^#]/ then content.gsub(/^/, indent) else content.gsub(/^#?/, indent) end end ## # Look for the given file in the directory containing the current file, # and then in each of the directories specified in the RDOC_INCLUDE path def find_include_file(name) to_search = [File.dirname(@input_file_name)].concat @include_path to_search.each do |dir| full_name = File.join(dir, name) stat = File.stat(full_name) rescue next return full_name if stat.readable? end nil end end to_ansi.rb000064400000004030147635633570006545 0ustar00## # Outputs RDoc markup with vibrant ANSI color! class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc ## # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color! def initialize markup = nil super @headings.clear @headings[1] = ["\e[1;32m", "\e[m"] # bold @headings[2] = ["\e[4;32m", "\e[m"] # underline @headings[3] = ["\e[32m", "\e[m"] # just green end ## # Maps attributes to ANSI sequences def init_tags add_tag :BOLD, "\e[1m", "\e[m" add_tag :TT, "\e[7m", "\e[m" add_tag :EM, "\e[4m", "\e[m" end ## # Overrides indent width to ensure output lines up correctly. def accept_list_item_end list_item width = case @list_type.last when :BULLET then 2 when :NOTE, :LABEL then if @prefix then @res << @prefix.strip @prefix = nil end @res << "\n" unless res.length == 1 2 else bullet = @list_index.last.to_s @list_index[-1] = @list_index.last.succ bullet.length + 2 end @indent -= width end ## # Adds coloring to note and label list items def accept_list_item_start list_item bullet = case @list_type.last when :BULLET then '*' when :NOTE, :LABEL then labels = Array(list_item.label).map do |label| attributes(label).strip end.join "\n" labels << ":\n" unless labels.empty? labels else @list_index.last.to_s + '.' end case @list_type.last when :NOTE, :LABEL then @indent += 2 @prefix = bullet + (' ' * @indent) else @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1) width = bullet.gsub(/\e\[[\d;]*m/, '').length + 1 @indent += width end end ## # Starts accepting with a reset screen def start_accepting super @res = ["\e[0m"] end end to_table_of_contents.rb000064400000003231147635633570011305 0ustar00## # Extracts just the RDoc::Markup::Heading elements from a # RDoc::Markup::Document to help build a table of contents class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter @to_toc = nil ## # Singleton for table-of-contents generation def self.to_toc @to_toc ||= new end ## # Output accumulator attr_reader :res ## # Omits headings with a level less than the given level. attr_accessor :omit_headings_below def initialize # :nodoc: super nil @omit_headings_below = nil end ## # Adds +document+ to the output, using its heading cutoff if present def accept_document document @omit_headings_below = document.omit_headings_below super end ## # Adds +heading+ to the table of contents def accept_heading heading @res << heading unless suppressed? heading end ## # Returns the table of contents def end_accepting @res end ## # Prepares the visitor for text generation def start_accepting @omit_headings_below = nil @res = [] end ## # Returns true if +heading+ is below the display threshold def suppressed? heading return false unless @omit_headings_below heading.level > @omit_headings_below end # :stopdoc: alias accept_block_quote ignore alias accept_raw ignore alias accept_rule ignore alias accept_blank_line ignore alias accept_paragraph ignore alias accept_verbatim ignore alias accept_list_end ignore alias accept_list_item_start ignore alias accept_list_item_end ignore alias accept_list_end_bullet ignore alias accept_list_start ignore # :startdoc: end indented_paragraph.rb000064400000001554147635633570010740 0ustar00## # An Indented Paragraph of text class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw ## # The indent in number of spaces attr_reader :indent ## # Creates a new IndentedParagraph containing +parts+ indented with +indent+ # spaces def initialize indent, *parts @indent = indent super(*parts) end def == other # :nodoc: super and indent == other.indent end ## # Calls #accept_indented_paragraph on +visitor+ def accept visitor visitor.accept_indented_paragraph self end ## # Joins the raw paragraph text and converts inline HardBreaks to the # +hard_break+ text followed by the indent. def text hard_break = nil @parts.map do |part| if RDoc::Markup::HardBreak === part then '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break else part end end.join end end attributes.rb000064400000002240147635633570007300 0ustar00## # We manage a set of attributes. Each attribute has a symbol name and a bit # value. class RDoc::Markup::Attributes ## # The special attribute type. See RDoc::Markup#add_special attr_reader :special ## # Creates a new attributes set. def initialize @special = 1 @name_to_bitmap = [ [:_SPECIAL_, @special], ] @next_bitmap = @special << 1 end ## # Returns a unique bit for +name+ def bitmap_for name bitmap = @name_to_bitmap.assoc name unless bitmap then bitmap = @next_bitmap @next_bitmap <<= 1 @name_to_bitmap << [name, bitmap] else bitmap = bitmap.last end bitmap end ## # Returns a string representation of +bitmap+ def as_string bitmap return 'none' if bitmap.zero? res = [] @name_to_bitmap.each do |name, bit| res << name if (bitmap & bit) != 0 end res.join ',' end ## # yields each attribute name in +bitmap+ def each_name_of bitmap return enum_for __method__, bitmap unless block_given? @name_to_bitmap.each do |name, bit| next if bit == @special yield name.to_s if (bitmap & bit) != 0 end end end list_item.rb000064400000003274147635633570007113 0ustar00## # An item within a List that contains paragraphs, headings, etc. # # For BULLET, NUMBER, LALPHA and UALPHA lists, the label will always be nil. # For NOTE and LABEL lists, the list label may contain: # # * a single String for a single label # * an Array of Strings for a list item with multiple terms # * nil for an extra description attached to a previously labeled list item class RDoc::Markup::ListItem ## # The label for the ListItem attr_accessor :label ## # Parts of the ListItem attr_reader :parts ## # Creates a new ListItem with an optional +label+ containing +parts+ def initialize label = nil, *parts @label = label @parts = [] @parts.concat parts end ## # Appends +part+ to the ListItem def << part @parts << part end def == other # :nodoc: self.class == other.class and @label == other.label and @parts == other.parts end ## # Runs this list item and all its #parts through +visitor+ def accept visitor visitor.accept_list_item_start self @parts.each do |part| part.accept visitor end visitor.accept_list_item_end self end ## # Is the ListItem empty? def empty? @parts.empty? end ## # Length of parts in the ListItem def length @parts.length end def pretty_print q # :nodoc: q.group 2, '[item: ', ']' do case @label when Array then q.pp @label q.text ';' q.breakable when String then q.pp @label q.text ';' q.breakable end q.seplist @parts do |part| q.pp part end end end ## # Adds +parts+ to the ListItem def push *parts @parts.concat parts end end text_formatter_test_case.rb000064400000004761147635633570012225 0ustar00## # Test case for creating new plain-text RDoc::Markup formatters. See also # RDoc::Markup::FormatterTestCase # # See test_rdoc_markup_to_rdoc.rb for a complete example. # # Example: # # class TestRDocMarkupToNewTextFormat < RDoc::Markup::TextFormatterTestCase # # add_visitor_tests # add_text_tests # # def setup # super # # @to = RDoc::Markup::ToNewTextFormat.new # end # # def accept_blank_line # assert_equal :junk, @to.res.join # end # # # ... # # end class RDoc::Markup::TextFormatterTestCase < RDoc::Markup::FormatterTestCase ## # Adds test cases to the calling TestCase. def self.add_text_tests self.class_eval do ## # Test case that calls @to.accept_heading def test_accept_heading_indent @to.start_accepting @to.indent = 3 @to.accept_heading @RM::Heading.new(1, 'Hello') accept_heading_indent end ## # Test case that calls @to.accept_rule def test_accept_rule_indent @to.start_accepting @to.indent = 3 @to.accept_rule @RM::Rule.new(1) accept_rule_indent end ## # Test case that calls @to.accept_verbatim def test_accept_verbatim_indent @to.start_accepting @to.indent = 2 @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n") accept_verbatim_indent end ## # Test case that calls @to.accept_verbatim with a big indent def test_accept_verbatim_big_indent @to.start_accepting @to.indent = 2 @to.accept_verbatim @RM::Verbatim.new("hi\n", "world\n") accept_verbatim_big_indent end ## # Test case that calls @to.accept_paragraph with an indent def test_accept_paragraph_indent @to.start_accepting @to.indent = 3 @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip) accept_paragraph_indent end ## # Test case that calls @to.accept_paragraph with a long line def test_accept_paragraph_wrap @to.start_accepting @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip) accept_paragraph_wrap end ## # Test case that calls @to.attributes with an escaped # cross-reference. If this test doesn't pass something may be very # wrong. def test_attributes assert_equal 'Dog', @to.attributes("\\Dog") end end end end attribute_manager.rb000064400000017527147635633570010625 0ustar00## # Manages changes of attributes in a block of text class RDoc::Markup::AttributeManager ## # The NUL character NULL = "\000".freeze #-- # We work by substituting non-printing characters in to the text. For now # I'm assuming that I can substitute a character in the range 0..8 for a 7 # bit character without damaging the encoded string, but this might be # optimistic #++ A_PROTECT = 004 # :nodoc: ## # Special mask character to prevent inline markup handling PROTECT_ATTR = A_PROTECT.chr # :nodoc: ## # The attributes enabled for this markup object. attr_reader :attributes ## # This maps delimiters that occur around words (such as *bold* or +tt+) # where the start and end delimiters and the same. This lets us optimize # the regexp attr_reader :matching_word_pairs ## # And this is used when the delimiters aren't the same. In this case the # hash maps a pattern to the attribute character attr_reader :word_pair_map ## # This maps HTML tags to the corresponding attribute char attr_reader :html_tags ## # A \ in front of a character that would normally be processed turns off # processing. We do this by turning \< into <#{PROTECT} attr_reader :protectable ## # And this maps _special_ sequences to a name. A special sequence is # something like a WikiWord attr_reader :special ## # Creates a new attribute manager that understands bold, emphasized and # teletype text. def initialize @html_tags = {} @matching_word_pairs = {} @protectable = %w[<] @special = [] @word_pair_map = {} @attributes = RDoc::Markup::Attributes.new add_word_pair "*", "*", :BOLD add_word_pair "_", "_", :EM add_word_pair "+", "+", :TT add_html "em", :EM add_html "i", :EM add_html "b", :BOLD add_html "tt", :TT add_html "code", :TT end ## # Return an attribute object with the given turn_on and turn_off bits set def attribute(turn_on, turn_off) RDoc::Markup::AttrChanger.new turn_on, turn_off end ## # Changes the current attribute from +current+ to +new+ def change_attribute current, new diff = current ^ new attribute(new & diff, current & diff) end ## # Used by the tests to change attributes by name from +current_set+ to # +new_set+ def changed_attribute_by_name current_set, new_set current = new = 0 current_set.each do |name| current |= @attributes.bitmap_for(name) end new_set.each do |name| new |= @attributes.bitmap_for(name) end change_attribute(current, new) end ## # Copies +start_pos+ to +end_pos+ from the current string def copy_string(start_pos, end_pos) res = @str[start_pos...end_pos] res.gsub!(/\000/, '') res end ## # Map attributes like textto the sequence # \001\002\001\003, where is a per-attribute specific # character def convert_attrs(str, attrs) # first do matching ones tags = @matching_word_pairs.keys.join("") re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/ 1 while str.gsub!(re) do attr = @matching_word_pairs[$2] attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr) $1 + NULL * $2.length + $3 + NULL * $2.length + $4 end # then non-matching unless @word_pair_map.empty? then @word_pair_map.each do |regexp, attr| str.gsub!(regexp) { attrs.set_attrs($`.length + $1.length, $2.length, attr) NULL * $1.length + $2 + NULL * $3.length } end end end ## # Converts HTML tags to RDoc attributes def convert_html(str, attrs) tags = @html_tags.keys.join '|' 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { attr = @html_tags[$1.downcase] html_length = $1.length + 2 seq = NULL * html_length attrs.set_attrs($`.length + html_length, $2.length, attr) seq + $2 + seq + NULL } end ## # Converts special sequences to RDoc attributes def convert_specials str, attrs @special.each do |regexp, attribute| str.scan(regexp) do capture = $~.size == 1 ? 0 : 1 s, e = $~.offset capture attrs.set_attrs s, e - s, attribute | @attributes.special end end end ## # Escapes special sequences of text to prevent conversion to RDoc def mask_protected_sequences # protect __send__, __FILE__, etc. @str.gsub!(/__([a-z]+)__/i, "_#{PROTECT_ATTR}_#{PROTECT_ATTR}\\1_#{PROTECT_ATTR}_#{PROTECT_ATTR}") @str.gsub!(/\\([#{Regexp.escape @protectable.join('')}])/, "\\1#{PROTECT_ATTR}") end ## # Unescapes special sequences of text def unmask_protected_sequences @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000") end ## # Adds a markup class with +name+ for words wrapped in the +start+ and # +stop+ character. To make words wrapped with "*" bold: # # am.add_word_pair '*', '*', :BOLD def add_word_pair(start, stop, name) raise ArgumentError, "Word flags may not start with '<'" if start[0,1] == '<' bitmap = @attributes.bitmap_for name if start == stop then @matching_word_pairs[start] = bitmap else pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/ @word_pair_map[pattern] = bitmap end @protectable << start[0,1] @protectable.uniq! end ## # Adds a markup class with +name+ for words surrounded by HTML tag +tag+. # To process emphasis tags: # # am.add_html 'em', :EM def add_html(tag, name) @html_tags[tag.downcase] = @attributes.bitmap_for name end ## # Adds a special handler for +pattern+ with +name+. A simple URL handler # would be: # # @am.add_special(/((https?:)\S+\w)/, :HYPERLINK) def add_special pattern, name @special << [pattern, @attributes.bitmap_for(name)] end ## # Processes +str+ converting attributes, HTML and specials def flow str @str = str mask_protected_sequences @attrs = RDoc::Markup::AttrSpan.new @str.length convert_attrs @str, @attrs convert_html @str, @attrs convert_specials @str, @attrs unmask_protected_sequences split_into_flow end ## # Debug method that prints a string along with its attributes def display_attributes puts puts @str.tr(NULL, "!") bit = 1 16.times do |bno| line = "" @str.length.times do |i| if (@attrs[i] & bit) == 0 line << " " else if bno.zero? line << "S" else line << ("%d" % (bno+1)) end end end puts(line) unless line =~ /^ *$/ bit <<= 1 end end ## # Splits the string into chunks by attribute change def split_into_flow res = [] current_attr = 0 str_len = @str.length # skip leading invisible text i = 0 i += 1 while i < str_len and @str[i].chr == "\0" start_pos = i # then scan the string, chunking it on attribute changes while i < str_len new_attr = @attrs[i] if new_attr != current_attr if i > start_pos res << copy_string(start_pos, i) start_pos = i end res << change_attribute(current_attr, new_attr) current_attr = new_attr if (current_attr & @attributes.special) != 0 then i += 1 while i < str_len and (@attrs[i] & @attributes.special) != 0 res << RDoc::Markup::Special.new(current_attr, copy_string(start_pos, i)) start_pos = i next end end # move on, skipping any invisible characters begin i += 1 end while i < str_len and @str[i].chr == "\0" end # tidy up trailing text if start_pos < str_len res << copy_string(start_pos, str_len) end # and reset to all attributes off res << change_attribute(current_attr, 0) if current_attr != 0 res end end list.rb000064400000003447147635633570006077 0ustar00## # A List is a homogeneous set of ListItems. # # The supported list types include: # # :BULLET:: # An unordered list # :LABEL:: # An unordered definition list, but using an alternate RDoc::Markup syntax # :LALPHA:: # An ordered list using increasing lowercase English letters # :NOTE:: # An unordered definition list # :NUMBER:: # An ordered list using increasing Arabic numerals # :UALPHA:: # An ordered list using increasing uppercase English letters # # Definition lists behave like HTML definition lists. Each list item can # describe multiple terms. See RDoc::Markup::ListItem for how labels and # definition are stored as list items. class RDoc::Markup::List ## # The list's type attr_accessor :type ## # Items in the list attr_reader :items ## # Creates a new list of +type+ with +items+. Valid list types are: # +:BULLET+, +:LABEL+, +:LALPHA+, +:NOTE+, +:NUMBER+, +:UALPHA+ def initialize type = nil, *items @type = type @items = [] @items.concat items end ## # Appends +item+ to the list def << item @items << item end def == other # :nodoc: self.class == other.class and @type == other.type and @items == other.items end ## # Runs this list and all its #items through +visitor+ def accept visitor visitor.accept_list_start self @items.each do |item| item.accept visitor end visitor.accept_list_end self end ## # Is the list empty? def empty? @items.empty? end ## # Returns the last item in the list def last @items.last end def pretty_print q # :nodoc: q.group 2, "[list: #{@type} ", ']' do q.seplist @items do |item| q.pp item end end end ## # Appends +items+ to the list def push *items @items.concat items end end to_test.rb000064400000002160147635633570006574 0ustar00## # This Markup outputter is used for testing purposes. class RDoc::Markup::ToTest < RDoc::Markup::Formatter # :stopdoc: ## # :section: Visitor def start_accepting @res = [] @list = [] end def end_accepting @res end def accept_paragraph(paragraph) @res << convert_flow(@am.flow(paragraph.text)) end def accept_raw raw @res << raw.parts.join end def accept_verbatim(verbatim) @res << verbatim.text.gsub(/^(\S)/, ' \1') end def accept_list_start(list) @list << case list.type when :BULLET then '*' when :NUMBER then '1' else list.type end end def accept_list_end(list) @list.pop end def accept_list_item_start(list_item) @res << "#{' ' * (@list.size - 1)}#{@list.last}: " end def accept_list_item_end(list_item) end def accept_blank_line(blank_line) @res << "\n" end def accept_heading(heading) @res << "#{'=' * heading.level} #{heading.text}" end def accept_rule(rule) @res << '-' * rule.weight end # :startdoc: end to_joined_paragraph.rb000064400000003062147635633570011114 0ustar00## # Joins the parts of an RDoc::Markup::Paragraph into a single String. # # This allows for easier maintenance and testing of Markdown support. # # This formatter only works on Paragraph instances. Attempting to process # other markup syntax items will not work. class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter def initialize # :nodoc: super nil end def start_accepting # :nodoc: end def end_accepting # :nodoc: end ## # Converts the parts of +paragraph+ to a single entry. def accept_paragraph paragraph parts = [] string = false paragraph.parts.each do |part| if String === part then if string then string << part else parts << part string = part end else parts << part string = false end end parts = parts.map do |part| if String === part then part.rstrip else part end end # TODO use Enumerable#chunk when ruby 1.8 support is dropped #parts = paragraph.parts.chunk do |part| # String === part #end.map do |string, chunk| # string ? chunk.join.rstrip : chunk #end.flatten paragraph.parts.replace parts end alias accept_block_quote ignore alias accept_heading ignore alias accept_list_end ignore alias accept_list_item_end ignore alias accept_list_item_start ignore alias accept_list_start ignore alias accept_raw ignore alias accept_rule ignore alias accept_verbatim ignore end blank_line.rb000064400000000551147635633570007213 0ustar00## # An empty line. This class is a singleton. class RDoc::Markup::BlankLine @instance = new ## # RDoc::Markup::BlankLine is a singleton def self.new @instance end ## # Calls #accept_blank_line on +visitor+ def accept visitor visitor.accept_blank_line self end def pretty_print q # :nodoc: q.text 'blankline' end end heading.rb000064400000002255147635633570006517 0ustar00## # A heading with a level (1-6) and text RDoc::Markup::Heading = Struct.new :level, :text do @to_html = nil @to_label = nil ## # A singleton RDoc::Markup::ToLabel formatter for headings. def self.to_label @to_label ||= RDoc::Markup::ToLabel.new end ## # A singleton plain HTML formatter for headings. Used for creating labels # for the Table of Contents def self.to_html return @to_html if @to_html markup = RDoc::Markup.new markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF @to_html = RDoc::Markup::ToHtml.new nil def @to_html.handle_special_CROSSREF special special.text.sub(/^\\/, '') end @to_html end ## # Calls #accept_heading on +visitor+ def accept visitor visitor.accept_heading self end ## # An HTML-safe anchor reference for this header. def aref "label-#{self.class.to_label.convert text.dup}" end ## # HTML markup of the text of this label without the surrounding header # element. def plain_html self.class.to_html.to_html(text.dup) end def pretty_print q # :nodoc: q.group 2, "[head: #{level} ", ']' do q.pp text end end end attr_span.rb000064400000000726147635633570007114 0ustar00## # An array of attributes which parallels the characters in a string. class RDoc::Markup::AttrSpan ## # Creates a new AttrSpan for +length+ characters def initialize(length) @attrs = Array.new(length, 0) end ## # Toggles +bits+ from +start+ to +length+ def set_attrs(start, length, bits) for i in start ... (start+length) @attrs[i] |= bits end end ## # Accesses flags for character +n+ def [](n) @attrs[n] end end attr_changer.rb000064400000000612147635633570007554 0ustar00class RDoc::Markup AttrChanger = Struct.new :turn_on, :turn_off # :nodoc: end ## # An AttrChanger records a change in attributes. It contains a bitmap of the # attributes to turn on, and a bitmap of those to turn off. class RDoc::Markup::AttrChanger def to_s # :nodoc: "Attr: +#{turn_on}/-#{turn_off}" end def inspect # :nodoc: '+%d/-%d' % [turn_on, turn_off] end end to_html_snippet.rb000064400000012660147635633570010331 0ustar00## # Outputs RDoc markup as paragraphs with inline markup only. class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml ## # After this many characters the input will be cut off. attr_reader :character_limit ## # The number of characters seen so far. attr_reader :characters # :nodoc: ## # The attribute bitmask attr_reader :mask ## # After this many paragraphs the input will be cut off. attr_reader :paragraph_limit ## # Count of paragraphs found attr_reader :paragraphs ## # Creates a new ToHtmlSnippet formatter that will cut off the input on the # next word boundary after the given number of +characters+ or +paragraphs+ # of text have been encountered. def initialize options, characters = 100, paragraphs = 3, markup = nil super options, markup @character_limit = characters @paragraph_limit = paragraphs @characters = 0 @mask = 0 @paragraphs = 0 @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF end ## # Adds +heading+ to the output as a paragraph def accept_heading heading @res << "

    #{to_html heading.text}\n" add_paragraph end ## # Raw sections are untrusted and ignored alias accept_raw ignore ## # Rules are ignored alias accept_rule ignore def accept_paragraph paragraph para = @in_list_entry.last || "

    " text = paragraph.text @hard_break @res << "#{para}#{wrap to_html text}\n" add_paragraph end ## # Finishes consumption of +list_item+ def accept_list_item_end list_item end ## # Prepares the visitor for consuming +list_item+ def accept_list_item_start list_item @res << list_item_start(list_item, @list.last) end ## # Prepares the visitor for consuming +list+ def accept_list_start list @list << list.type @res << html_list_name(list.type, true) @in_list_entry.push '' end ## # Adds +verbatim+ to the output def accept_verbatim verbatim throw :done if @characters >= @character_limit input = verbatim.text.rstrip text = truncate input text << ' ...' unless text == input super RDoc::Markup::Verbatim.new text add_paragraph end ## # Prepares the visitor for HTML snippet generation def start_accepting super @characters = 0 end ## # Removes escaping from the cross-references in +special+ def handle_special_CROSSREF special special.text.sub(/\A\\/, '') end ## # +special+ is a
    def handle_special_HARD_BREAK special @characters -= 4 '
    ' end ## # Lists are paragraphs, but notes and labels have a separator def list_item_start list_item, list_type throw :done if @characters >= @character_limit case list_type when :BULLET, :LALPHA, :NUMBER, :UALPHA then "

    " when :LABEL, :NOTE then labels = Array(list_item.label).map do |label| to_html label end.join ', ' labels << " — " unless labels.empty? start = "

    #{labels}" @characters += 1 # try to include the label start else raise RDoc::Error, "Invalid list type: #{list_type.inspect}" end end ## # Returns just the text of +link+, +url+ is only used to determine the link # type. def gen_url url, text if url =~ /^rdoc-label:([^:]*)(?::(.*))?/ then type = "link" elsif url =~ /([A-Za-z]+):(.*)/ then type = $1 else type = "http" end if (type == "http" or type == "https" or type == "link") and url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then '' else text.sub(%r%^#{type}:/*%, '') end end ## # In snippets, there are no lists def html_list_name list_type, open_tag '' end ## # Throws +:done+ when paragraph_limit paragraphs have been encountered def add_paragraph @paragraphs += 1 throw :done if @paragraphs >= @paragraph_limit end ## # Marks up +content+ def convert content catch :done do return super end end_accepting end ## # Converts flow items +flow+ def convert_flow flow throw :done if @characters >= @character_limit res = [] @mask = 0 flow.each do |item| case item when RDoc::Markup::AttrChanger then off_tags res, item on_tags res, item when String then text = convert_string item res << truncate(text) when RDoc::Markup::Special then text = convert_special item res << truncate(text) else raise "Unknown flow element: #{item.inspect}" end if @characters >= @character_limit then off_tags res, RDoc::Markup::AttrChanger.new(0, @mask) break end end res << ' ...' if @characters >= @character_limit res.join end ## # Maintains a bitmask to allow HTML elements to be closed properly. See # RDoc::Markup::Formatter. def on_tags res, item @mask ^= item.turn_on super end ## # Maintains a bitmask to allow HTML elements to be closed properly. See # RDoc::Markup::Formatter. def off_tags res, item @mask ^= item.turn_off super end ## # Truncates +text+ at the end of the first word after the character_limit. def truncate text length = text.length characters = @characters @characters += length return text if @characters < @character_limit remaining = @character_limit - characters text =~ /\A(.{#{remaining},}?)(\s|$)/m # TODO word-break instead of \s? $1 end end formatter_test_case.rb000064400000041541147635633570011156 0ustar00require 'minitest/unit' ## # Test case for creating new RDoc::Markup formatters. See # test/test_rdoc_markup_to_*.rb for examples. # # This test case adds a variety of tests to your subclass when # #add_visitor_tests is called. Most tests set up a scenario then call a # method you will provide to perform the assertion on the output. # # Your subclass must instantiate a visitor and assign it to @to. # # For example, test_accept_blank_line sets up a RDoc::Markup::BlockLine then # calls accept_blank_line on your visitor. You are responsible for asserting # that the output is correct. # # Example: # # class TestRDocMarkupToNewFormat < RDoc::Markup::FormatterTestCase # # add_visitor_tests # # def setup # super # # @to = RDoc::Markup::ToNewFormat.new # end # # def accept_blank_line # assert_equal :junk, @to.res.join # end # # # ... # # end class RDoc::Markup::FormatterTestCase < RDoc::TestCase ## # Call #setup when inheriting from this test case. # # Provides the following instance variables: # # +@m+:: RDoc::Markup.new # +@RM+:: RDoc::Markup # to reduce typing # +@bullet_list+:: @RM::List.new :BULLET, # ... # +@label_list+:: @RM::List.new :LABEL, # ... # +@lalpha_list+:: @RM::List.new :LALPHA, # ... # +@note_list+:: @RM::List.new :NOTE, # ... # +@number_list+:: @RM::List.new :NUMBER, # ... # +@ualpha_list+:: @RM::List.new :UALPHA, # ... def setup super @options = RDoc::Options.new @m = @RM.new @bullet_list = @RM::List.new(:BULLET, @RM::ListItem.new(nil, @RM::Paragraph.new('l1')), @RM::ListItem.new(nil, @RM::Paragraph.new('l2'))) @label_list = @RM::List.new(:LABEL, @RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')), @RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too'))) @lalpha_list = @RM::List.new(:LALPHA, @RM::ListItem.new(nil, @RM::Paragraph.new('l1')), @RM::ListItem.new(nil, @RM::Paragraph.new('l2'))) @note_list = @RM::List.new(:NOTE, @RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')), @RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too'))) @number_list = @RM::List.new(:NUMBER, @RM::ListItem.new(nil, @RM::Paragraph.new('l1')), @RM::ListItem.new(nil, @RM::Paragraph.new('l2'))) @ualpha_list = @RM::List.new(:UALPHA, @RM::ListItem.new(nil, @RM::Paragraph.new('l1')), @RM::ListItem.new(nil, @RM::Paragraph.new('l2'))) end ## # Call to add the visitor tests to your test case def self.add_visitor_tests class_eval do ## # Calls start_accepting which needs to verify startup state def test_start_accepting @to.start_accepting start_accepting end ## # Calls end_accepting on your test case which needs to call # @to.end_accepting and verify document generation def test_end_accepting @to.start_accepting @to.res << 'hi' end_accepting end ## # Calls accept_blank_line def test_accept_blank_line @to.start_accepting @to.accept_blank_line @RM::BlankLine.new accept_blank_line end ## # Calls accept_block_quote def test_accept_block_quote @to.start_accepting @to.accept_block_quote block para 'quote' accept_block_quote end ## # Test case that calls @to.accept_document def test_accept_document @to.start_accepting @to.accept_document @RM::Document.new @RM::Paragraph.new 'hello' accept_document end ## # Calls accept_heading with a level 5 RDoc::Markup::Heading def test_accept_heading @to.start_accepting @to.accept_heading @RM::Heading.new(5, 'Hello') accept_heading end ## # Calls accept_heading_1 with a level 1 RDoc::Markup::Heading def test_accept_heading_1 @to.start_accepting @to.accept_heading @RM::Heading.new(1, 'Hello') accept_heading_1 end ## # Calls accept_heading_2 with a level 2 RDoc::Markup::Heading def test_accept_heading_2 @to.start_accepting @to.accept_heading @RM::Heading.new(2, 'Hello') accept_heading_2 end ## # Calls accept_heading_3 with a level 3 RDoc::Markup::Heading def test_accept_heading_3 # HACK this doesn't belong here skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars @to.start_accepting @to.accept_heading @RM::Heading.new(3, 'Hello') accept_heading_3 end ## # Calls accept_heading_4 with a level 4 RDoc::Markup::Heading def test_accept_heading_4 @to.start_accepting @to.accept_heading @RM::Heading.new(4, 'Hello') accept_heading_4 end ## # Calls accept_heading_b with a bold level 1 RDoc::Markup::Heading def test_accept_heading_b @to.start_accepting @to.accept_heading @RM::Heading.new(1, '*Hello*') accept_heading_b end ## # Calls accept_heading_suppressed_crossref with a level 1 # RDoc::Markup::Heading containing a suppressed crossref def test_accept_heading_suppressed_crossref # HACK to_html_crossref test @to.start_accepting @to.accept_heading @RM::Heading.new(1, '\\Hello') accept_heading_suppressed_crossref end ## # Calls accept_paragraph def test_accept_paragraph @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('hi') accept_paragraph end ## # Calls accept_paragraph_b with a RDoc::Markup::Paragraph containing # bold words def test_accept_paragraph_b @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('reg bold words reg') accept_paragraph_b end ## # Calls accept_paragraph_br with a RDoc::Markup::Paragraph containing # a \
    def test_accept_paragraph_br @to.start_accepting @to.accept_paragraph para 'one
    two' accept_paragraph_br end ## # Calls accept_paragraph with a Paragraph containing a hard break def test_accept_paragraph_break @to.start_accepting @to.accept_paragraph para('hello', hard_break, 'world') accept_paragraph_break end ## # Calls accept_paragraph_i with a RDoc::Markup::Paragraph containing # emphasized words def test_accept_paragraph_i @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('reg italic words reg') accept_paragraph_i end ## # Calls accept_paragraph_plus with a RDoc::Markup::Paragraph containing # teletype words def test_accept_paragraph_plus @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('reg +teletype+ reg') accept_paragraph_plus end ## # Calls accept_paragraph_star with a RDoc::Markup::Paragraph containing # bold words def test_accept_paragraph_star @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('reg *bold* reg') accept_paragraph_star end ## # Calls accept_paragraph_underscore with a RDoc::Markup::Paragraph # containing emphasized words def test_accept_paragraph_underscore @to.start_accepting @to.accept_paragraph @RM::Paragraph.new('reg _italic_ reg') accept_paragraph_underscore end ## # Calls accept_verbatim with a RDoc::Markup::Verbatim def test_accept_verbatim @to.start_accepting @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n") accept_verbatim end ## # Calls accept_raw with a RDoc::Markup::Raw def test_accept_raw @to.start_accepting @to.accept_raw @RM::Raw.new("", "
    NameCount", "
    a1", "
    b2", "
    ") accept_raw end ## # Calls accept_rule with a RDoc::Markup::Rule def test_accept_rule @to.start_accepting @to.accept_rule @RM::Rule.new(4) accept_rule end ## # Calls accept_list_item_start_bullet def test_accept_list_item_start_bullet @to.start_accepting @to.accept_list_start @bullet_list @to.accept_list_item_start @bullet_list.items.first accept_list_item_start_bullet end ## # Calls accept_list_item_start_label def test_accept_list_item_start_label @to.start_accepting @to.accept_list_start @label_list @to.accept_list_item_start @label_list.items.first accept_list_item_start_label end ## # Calls accept_list_item_start_lalpha def test_accept_list_item_start_lalpha @to.start_accepting @to.accept_list_start @lalpha_list @to.accept_list_item_start @lalpha_list.items.first accept_list_item_start_lalpha end ## # Calls accept_list_item_start_note def test_accept_list_item_start_note @to.start_accepting @to.accept_list_start @note_list @to.accept_list_item_start @note_list.items.first accept_list_item_start_note end ## # Calls accept_list_item_start_note_2 def test_accept_list_item_start_note_2 list = list(:NOTE, item('teletype', para('teletype description'))) @to.start_accepting list.accept @to @to.end_accepting accept_list_item_start_note_2 end ## # Calls accept_list_item_start_note_multi_description def test_accept_list_item_start_note_multi_description list = list(:NOTE, item(%w[label], para('description one')), item(nil, para('description two'))) @to.start_accepting list.accept @to @to.end_accepting accept_list_item_start_note_multi_description end ## # Calls accept_list_item_start_note_multi_label def test_accept_list_item_start_note_multi_label list = list(:NOTE, item(%w[one two], para('two headers'))) @to.start_accepting list.accept @to @to.end_accepting accept_list_item_start_note_multi_label end ## # Calls accept_list_item_start_number def test_accept_list_item_start_number @to.start_accepting @to.accept_list_start @number_list @to.accept_list_item_start @number_list.items.first accept_list_item_start_number end ## # Calls accept_list_item_start_ualpha def test_accept_list_item_start_ualpha @to.start_accepting @to.accept_list_start @ualpha_list @to.accept_list_item_start @ualpha_list.items.first accept_list_item_start_ualpha end ## # Calls accept_list_item_end_bullet def test_accept_list_item_end_bullet @to.start_accepting @to.accept_list_start @bullet_list @to.accept_list_item_start @bullet_list.items.first @to.accept_list_item_end @bullet_list.items.first accept_list_item_end_bullet end ## # Calls accept_list_item_end_label def test_accept_list_item_end_label @to.start_accepting @to.accept_list_start @label_list @to.accept_list_item_start @label_list.items.first @to.accept_list_item_end @label_list.items.first accept_list_item_end_label end ## # Calls accept_list_item_end_lalpha def test_accept_list_item_end_lalpha @to.start_accepting @to.accept_list_start @lalpha_list @to.accept_list_item_start @lalpha_list.items.first @to.accept_list_item_end @lalpha_list.items.first accept_list_item_end_lalpha end ## # Calls accept_list_item_end_note def test_accept_list_item_end_note @to.start_accepting @to.accept_list_start @note_list @to.accept_list_item_start @note_list.items.first @to.accept_list_item_end @note_list.items.first accept_list_item_end_note end ## # Calls accept_list_item_end_number def test_accept_list_item_end_number @to.start_accepting @to.accept_list_start @number_list @to.accept_list_item_start @number_list.items.first @to.accept_list_item_end @number_list.items.first accept_list_item_end_number end ## # Calls accept_list_item_end_ualpha def test_accept_list_item_end_ualpha @to.start_accepting @to.accept_list_start @ualpha_list @to.accept_list_item_start @ualpha_list.items.first @to.accept_list_item_end @ualpha_list.items.first accept_list_item_end_ualpha end ## # Calls accept_list_start_bullet def test_accept_list_start_bullet @to.start_accepting @to.accept_list_start @bullet_list accept_list_start_bullet end ## # Calls accept_list_start_label def test_accept_list_start_label @to.start_accepting @to.accept_list_start @label_list accept_list_start_label end ## # Calls accept_list_start_lalpha def test_accept_list_start_lalpha @to.start_accepting @to.accept_list_start @lalpha_list accept_list_start_lalpha end ## # Calls accept_list_start_note def test_accept_list_start_note @to.start_accepting @to.accept_list_start @note_list accept_list_start_note end ## # Calls accept_list_start_number def test_accept_list_start_number @to.start_accepting @to.accept_list_start @number_list accept_list_start_number end ## # Calls accept_list_start_ualpha def test_accept_list_start_ualpha @to.start_accepting @to.accept_list_start @ualpha_list accept_list_start_ualpha end ## # Calls accept_list_end_bullet def test_accept_list_end_bullet @to.start_accepting @to.accept_list_start @bullet_list @to.accept_list_end @bullet_list accept_list_end_bullet end ## # Calls accept_list_end_label def test_accept_list_end_label @to.start_accepting @to.accept_list_start @label_list @to.accept_list_end @label_list accept_list_end_label end ## # Calls accept_list_end_lalpha def test_accept_list_end_lalpha @to.start_accepting @to.accept_list_start @lalpha_list @to.accept_list_end @lalpha_list accept_list_end_lalpha end ## # Calls accept_list_end_number def test_accept_list_end_number @to.start_accepting @to.accept_list_start @number_list @to.accept_list_end @number_list accept_list_end_number end ## # Calls accept_list_end_note def test_accept_list_end_note @to.start_accepting @to.accept_list_start @note_list @to.accept_list_end @note_list accept_list_end_note end ## # Calls accept_list_end_ualpha def test_accept_list_end_ualpha @to.start_accepting @to.accept_list_start @ualpha_list @to.accept_list_end @ualpha_list accept_list_end_ualpha end ## # Calls list_nested with a two-level list def test_list_nested doc = @RM::Document.new( @RM::List.new(:BULLET, @RM::ListItem.new(nil, @RM::Paragraph.new('l1'), @RM::List.new(:BULLET, @RM::ListItem.new(nil, @RM::Paragraph.new('l1.1')))), @RM::ListItem.new(nil, @RM::Paragraph.new('l2')))) doc.accept @to list_nested end ## # Calls list_verbatim with a list containing a verbatim block def test_list_verbatim # HACK overblown doc = doc( list(:BULLET, item(nil, para('list stuff'), blank_line, verb("* list\n", " with\n", "\n", " second\n", "\n", " 1. indented\n", " 2. numbered\n", "\n", " third\n", "\n", "* second\n")))) doc.accept @to list_verbatim end end end end include.rb000064400000001437147635633570006544 0ustar00## # A file included at generation time. Objects of this class are created by # RDoc::RD for an extension-less include. # # This implementation in incomplete. class RDoc::Markup::Include ## # The filename to be included, without extension attr_reader :file ## # Directories to search for #file attr_reader :include_path ## # Creates a new include that will import +file+ from +include_path+ def initialize file, include_path @file = file @include_path = include_path end def == other # :nodoc: self.class === other and @file == other.file and @include_path == other.include_path end def pretty_print q # :nodoc: q.group 2, '[incl ', ']' do q.text file q.breakable q.text 'from ' q.pp include_path end end end