diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/rdoc.rb | 243 | ||||
-rw-r--r-- | lib/rdoc/markup.rb | 29 | ||||
-rw-r--r-- | lib/rdoc/markup/attribute_manager.rb | 274 | ||||
-rw-r--r-- | lib/rdoc/markup/inline.rb | 266 | ||||
-rw-r--r-- | lib/rdoc/markup/to_html.rb | 3 | ||||
-rw-r--r-- | lib/rdoc/parsers/parse_rb.rb | 2 | ||||
-rw-r--r-- | lib/rdoc/rdoc.rb | 24 | ||||
-rw-r--r-- | lib/rdoc/ri/descriptions.rb | 3 | ||||
-rw-r--r-- | lib/rdoc/ri/display.rb | 238 | ||||
-rw-r--r-- | lib/rdoc/ri/driver.rb | 15 | ||||
-rw-r--r-- | lib/rdoc/ri/formatter.rb | 19 | ||||
-rw-r--r-- | lib/rdoc/template.rb | 8 |
12 files changed, 716 insertions, 408 deletions
diff --git a/lib/rdoc.rb b/lib/rdoc.rb index 25989189ad..d89ac1fde9 100644 --- a/lib/rdoc.rb +++ b/lib/rdoc.rb @@ -1,5 +1,246 @@ +$DEBUG_RDOC = nil + ## -# :include: rdoc/README +# = RDOC - Ruby Documentation System +# +# This package contains RDoc and RDoc::Markup. RDoc is an application that +# produces documentation for one or more Ruby source files. We work similarly +# to JavaDoc, parsing the source, and extracting the definition for classes, +# modules, and methods (along with includes and requires). We associate with +# these optional documentation contained in the immediately preceding comment +# block, and then render the result using a pluggable output formatter. +# RDoc::Markup is a library that converts plain text into various output +# formats. The markup library is used to interpret the comment blocks that +# RDoc uses to document methods, classes, and so on. +# +# == Roadmap +# +# * If you want to use RDoc to create documentation for your Ruby source files, +# read on. +# * If you want to include extensions written in C, see RDoc::C_Parser +# * For information on the various markups available in comment blocks, see +# RDoc::Markup. +# * If you want to drive RDoc programatically, see RDoc::RDoc. +# * If you want to use the library to format text blocks into HTML, have a look +# at RDoc::Markup. +# * If you want to try writing your own HTML output template, see +# RDoc::Generator::HTML +# +# == Summary +# +# Once installed, you can create documentation using the 'rdoc' command +# (the command is 'rdoc.bat' under Windows) +# +# % rdoc [options] [names...] +# +# Type "rdoc --help" for an up-to-date option summary. +# +# A typical use might be to generate documentation for a package of Ruby +# source (such as rdoc itself). +# +# % rdoc +# +# This command generates documentation for all the Ruby and C source +# files in and below the current directory. These will be stored in a +# documentation tree starting in the subdirectory 'doc'. +# +# You can make this slightly more useful for your readers by having the +# index page contain the documentation for the primary file. In our +# case, we could type +# +# % rdoc --main rdoc.rb +# +# You'll find information on the various formatting tricks you can use +# in comment blocks in the documentation this generates. +# +# RDoc uses file extensions to determine how to process each file. File names +# ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files +# ending +.c+ are parsed as C files. All other files are assumed to +# contain just Markup-style markup (with or without leading '#' comment +# markers). If directory names are passed to RDoc, they are scanned +# recursively for C and Ruby source files only. +# +# = Markup +# +# For information on how to make lists, hyperlinks, etc. with RDoc, see +# RDoc::Markup. +# +# Comment blocks can be written fairly naturally, either using '#' on +# successive lines of the comment, or by including the comment in +# an =begin/=end block. If you use the latter form, the =begin line must be +# flagged with an RDoc tag: +# +# =begin rdoc +# Documentation to be processed by RDoc. +# +# ... +# =end +# +# RDoc stops processing comments if it finds a comment line containing +# a <tt>--</tt>. This can be used to separate external from internal +# comments, or to stop a comment being associated with a method, class, or +# module. Commenting can be turned back on with a line that starts with a +# <tt>++</tt>. +# +# ## +# # Extract the age and calculate the date-of-birth. +# #-- +# # FIXME: fails if the birthday falls on February 29th +# #++ +# # The DOB is returned as a Time object. +# +# def get_dob(person) +# # ... +# end +# +# Names of classes, source files, and any method names containing an +# underscore or preceded by a hash character are automatically hyperlinked +# from comment text to their description. +# +# Method parameter lists are extracted and displayed with the method +# description. If a method calls +yield+, then the parameters passed to yield +# will also be displayed: +# +# def fred +# ... +# yield line, address +# +# This will get documented as: +# +# fred() { |line, address| ... } +# +# You can override this using a comment containing ':yields: ...' immediately +# after the method definition +# +# def fred # :yields: index, position +# # ... +# +# yield line, address +# +# which will get documented as +# +# fred() { |index, position| ... } +# +# +:yields:+ is an example of a documentation directive. These appear +# immediately after the start of the document element they are modifying. +# +# == Directives +# +# [+:nodoc:+ / +:nodoc:+ all] +# Don't include this element in the documentation. For classes +# and modules, the methods, aliases, constants, and attributes +# directly within the affected class or module will also be +# omitted. By default, though, modules and classes within that +# class of module _will_ be documented. This is turned off by +# adding the +all+ modifier. +# +# module MyModule # :nodoc: +# class Input +# end +# end +# +# module OtherModule # :nodoc: all +# class Output +# end +# end +# +# In the above code, only class +MyModule::Input+ will be documented. +# :nodoc: is global across all files the class or module appears in, so use +# :stopdoc:/:startdoc: to only omit documentation for a particular set of +# methods, etc. +# +# [+:doc:+] +# Force a method or attribute to be documented even if it wouldn't otherwise +# be. Useful if, for example, you want to include documentation of a +# particular private method. +# +# [+:notnew:+] +# Only applicable to the +initialize+ instance method. Normally RDoc +# assumes that the documentation and parameters for #initialize are +# actually for the ::new method, and so fakes out a ::new for the class. +# The :notnew: modifier stops this. Remember that #initialize is protected, +# so you won't see the documentation unless you use the -a command line +# option. +# +# Comment blocks can contain other directives: +# +# [<tt>:section: title</tt>] +# Starts a new section in the output. The title following +:section:+ is +# used as the section heading, and the remainder of the comment containing +# the section is used as introductory text. Subsequent methods, aliases, +# attributes, and classes will be documented in this section. A :section: +# comment block may have one or more lines before the :section: directive. +# These will be removed, and any identical lines at the end of the block are +# also removed. This allows you to add visual cues such as: +# +# # ---------------------------------------- +# # :section: My Section +# # This is the section that I wrote. +# # See it glisten in the noon-day sun. +# # ---------------------------------------- +# +# [+:call-seq:+] +# Lines up to the next blank line in the comment are treated as the method's +# calling sequence, overriding the default parsing of method parameters and +# yield arguments. +# +# [+:include:+ _filename_] +# \Include the contents of the named file at this point. The file will be +# searched for in the directories listed by the +--include+ option, or in +# the current directory by default. The contents of the file will be +# shifted to have the same indentation as the ':' at the start of the +# :include: directive. +# +# [+:title:+ _text_] +# Sets the title for the document. Equivalent to the <tt>--title</tt> +# command line parameter. (The command line parameter overrides any :title: +# directive in the source). +# +# [+:enddoc:+] +# Document nothing further at the current level. +# +# [+:main:+ _name_] +# Equivalent to the <tt>--main</tt> command line parameter. +# +# [+:stopdoc:+ / +:startdoc:+] +# Stop and start adding new documentation elements to the current container. +# For example, if a class has a number of constants that you don't want to +# document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the +# last. If you don't specifiy a +:startdoc:+ by the end of the container, +# disables documentation for the entire class or module. +# +# = Other stuff +# +# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net> +# +# Dave Thomas <dave@pragmaticprogrammer.com> is the original author of RDoc. +# +# == Credits +# +# * The Ruby parser in rdoc/parse.rb is based heavily on the outstanding +# work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby +# parser for irb and the rtags package. +# +# * Code to diagram classes and modules was written by Sergey A Yanovitsky +# (Jah) of Enticla. +# +# * Charset patch from MoonWolf. +# +# * Rich Kilmer wrote the kilmer.rb output template. +# +# * Dan Brickley led the design of the RDF format. +# +# == License +# +# RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It +# is free software, and may be redistributed under the terms specified +# in the README file of the Ruby distribution. +# +# == Warranty +# +# This software is provided "as is" and without any express or implied +# warranties, including, without limitation, the implied warranties of +# merchantibility and fitness for a particular purpose. module RDoc diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index 9334329d6b..fdd1a11736 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -11,8 +11,8 @@ require 'rdoc' # RDoc::Markup itself does no output formatting: this is left to a different # set of classes. # -# RDoc::Markup is extendable at runtime: you can add new markup elements to be -# recognised in the documents that RDoc::Markup parses. +# RDoc::Markup is extendable at runtime: you can add \new markup elements to +# be recognised in the documents that RDoc::Markup parses. # # RDoc::Markup is intended to be the basis for a family of tools which share # the common requirement that simple, plain-text should be rendered in a @@ -29,10 +29,9 @@ require 'rdoc' # paragraph. # # * If a paragraph starts with a "*", "-", or with "<digit>.", then it is -# taken to be the start of a list. The margin in increased to be the -# first non-space following the list start flag. Subsequent lines -# should be indented to this new margin until the list ends. For -# example: +# taken to be the start of a list. The margin in increased to be the first +# non-space following the list start flag. Subsequent lines should be +# indented to this \new margin until the list ends. For example: # # * this is a list with three paragraphs in # the first item. This is the first paragraph. @@ -102,7 +101,7 @@ require 'rdoc' # Unlike conventional Wiki markup, general markup can cross line # boundaries. You can turn off the interpretation of markup by # preceding the first character with a backslash, so \\\<b>bold -# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold +# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold* # respectively. # # * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are @@ -118,17 +117,15 @@ require 'rdoc' # # == Synopsis # -# This code converts <tt>input_string</tt> to HTML. The conversion -# takes place in the +convert+ method, so you can use the same -# RDoc::Markup object to convert multiple input strings. +# This code converts +input_string+ to HTML. The conversion takes place in +# the +convert+ method, so you can use the same RDoc::Markup converter to +# convert multiple input strings. # -# require 'rdoc/markup' # require 'rdoc/markup/to_html' # -# p = RDoc::Markup.new # h = RDoc::Markup::ToHtml.new # -# puts p.convert(input_string, h) +# puts h.convert(input_string) # # You can extend the RDoc::Markup parser to recognise new markup # sequences, and to add special processing for text that matches a @@ -152,10 +149,10 @@ require 'rdoc' # # m.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) # -# h = WikiHtml.new -# h.add_tag(:STRIKE, "<strike>", "</strike>") +# wh = WikiHtml.new +# wh.add_tag(:STRIKE, "<strike>", "</strike>") # -# puts "<body>" + m.convert(ARGF.read, h) + "</body>" +# puts "<body>#{wh.convert ARGF.read}</body>" # #-- # Author:: Dave Thomas, dave@pragmaticprogrammer.com diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb new file mode 100644 index 0000000000..72f70dadd7 --- /dev/null +++ b/lib/rdoc/markup/attribute_manager.rb @@ -0,0 +1,274 @@ +require 'rdoc/markup/inline' + +class RDoc::Markup::AttributeManager + + 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 + PROTECT_ATTR = A_PROTECT.chr + + ## + # 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 + + 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 + + WORD_PAIR_MAP = {} + + ## + # This maps HTML tags to the corresponding attribute char + + HTML_TAGS = {} + + ## + # And this maps _special_ sequences to a name. A special sequence is + # something like a WikiWord + + SPECIAL = {} + + ## + # 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 + + def change_attribute(current, new) + diff = current ^ new + attribute(new & diff, current & diff) + end + + def changed_attribute_by_name(current_set, new_set) + current = new = 0 + current_set.each do |name| + current |= RDoc::Markup::Attribute.bitmap_for(name) + end + + new_set.each do |name| + new |= RDoc::Markup::Attribute.bitmap_for(name) + end + + change_attribute(current, new) + end + + def copy_string(start_pos, end_pos) + res = @str[start_pos...end_pos] + res.gsub!(/\000/, '') + res + end + + ## + # Map attributes like <b>text</b>to the sequence + # \001\002<char>\001\003<char>, where <char> 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 + + 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 + + def convert_specials(str, attrs) + unless SPECIAL.empty? + SPECIAL.each do |regexp, attr| + str.scan(regexp) do + attrs.set_attrs($`.length, $&.length, + attr | RDoc::Markup::Attribute::SPECIAL) + end + end + end + end + + ## + # A \ in front of a character that would normally be processed turns off + # processing. We do this by turning \< into <#{PROTECT} + + PROTECTABLE = %w[<\\] + + def mask_protected_sequences + protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])") + @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}") + end + + def unmask_protected_sequences + @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000") + end + + def initialize + 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) + + add_special(/<!--(.*?)-->/, :COMMENT) + end + + def add_word_pair(start, stop, name) + raise ArgumentError, "Word flags may not start with '<'" if + start[0,1] == '<' + + bitmap = RDoc::Markup::Attribute.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 + + def add_html(tag, name) + HTML_TAGS[tag.downcase] = RDoc::Markup::Attribute.bitmap_for name + end + + def add_special(pattern, name) + SPECIAL[pattern] = RDoc::Markup::Attribute.bitmap_for name + end + + def flow(str) + @str = str + + puts("Before flow, str='#{@str.dump}'") if $DEBUG_RDOC + mask_protected_sequences + + @attrs = RDoc::Markup::AttrSpan.new @str.length + + puts("After protecting, str='#{@str.dump}'") if $DEBUG_RDOC + + convert_attrs(@str, @attrs) + convert_html(@str, @attrs) + convert_specials(str, @attrs) + + unmask_protected_sequences + + puts("After flow, str='#{@str.dump}'") if $DEBUG_RDOC + + return split_into_flow + end + + 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 + + def split_into_flow + display_attributes if $DEBUG_RDOC + + res = [] + current_attr = 0 + str = "" + + 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 & RDoc::Markup::Attribute::SPECIAL) != 0 then + i += 1 while + i < str_len and (@attrs[i] & RDoc::Markup::Attribute::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 + + return res + end + +end + diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb index 418f254998..ee77679a11 100644 --- a/lib/rdoc/markup/inline.rb +++ b/lib/rdoc/markup/inline.rb @@ -39,12 +39,12 @@ class RDoc::Markup end end + AttrChanger = Struct.new(:turn_on, :turn_off) + ## # 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. - AttrChanger = Struct.new(:turn_on, :turn_off) - class AttrChanger def to_s "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}" @@ -96,266 +96,6 @@ class RDoc::Markup end - class AttributeManager - - 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 - PROTECT_ATTR = A_PROTECT.chr - - ## - # 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 - - 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 - - WORD_PAIR_MAP = {} - - ## - # This maps HTML tags to the corresponding attribute char - - HTML_TAGS = {} - - ## - # And this maps _special_ sequences to a name. A special sequence is - # something like a WikiWord - - SPECIAL = {} - - ## - # Return an attribute object with the given turn_on and turn_off bits set - - def attribute(turn_on, turn_off) - AttrChanger.new(turn_on, turn_off) - end - - def change_attribute(current, new) - diff = current ^ new - attribute(new & diff, current & diff) - end - - def changed_attribute_by_name(current_set, new_set) - current = new = 0 - current_set.each {|name| current |= Attribute.bitmap_for(name) } - new_set.each {|name| new |= Attribute.bitmap_for(name) } - change_attribute(current, new) - end - - def copy_string(start_pos, end_pos) - res = @str[start_pos...end_pos] - res.gsub!(/\000/, '') - res - end - - ## - # Map attributes like <b>text</b>to the sequence - # \001\002<char>\001\003<char>, where <char> 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 - - 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 - - def convert_specials(str, attrs) - unless SPECIAL.empty? - SPECIAL.each do |regexp, attr| - str.scan(regexp) do - attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL) - end - end - end - end - - ## - # A \ in front of a character that would normally be processed turns off - # processing. We do this by turning \< into <#{PROTECT} - - PROTECTABLE = %w[<\\] - - def mask_protected_sequences - protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])") - @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}") - end - - def unmask_protected_sequences - @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000") - end - - def initialize - 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) - - add_special(/<!--(.*?)-->/, :COMMENT) - end - - def add_word_pair(start, stop, name) - raise "Word flags may not start '<'" if start[0] == ?< - bitmap = Attribute.bitmap_for(name) - if start == stop - MATCHING_WORD_PAIRS[start] = bitmap - else - pattern = Regexp.new("(" + Regexp.escape(start) + ")" + -# "([A-Za-z]+)" + - "(\\S+)" + - "(" + Regexp.escape(stop) +")") - WORD_PAIR_MAP[pattern] = bitmap - end - PROTECTABLE << start[0,1] - PROTECTABLE.uniq! - end - - def add_html(tag, name) - HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name) - end - - def add_special(pattern, name) - SPECIAL[pattern] = Attribute.bitmap_for(name) - end - - def flow(str) - @str = str - - puts("Before flow, str='#{@str.dump}'") if $DEBUG_RDOC - mask_protected_sequences - - @attrs = AttrSpan.new(@str.length) - - puts("After protecting, str='#{@str.dump}'") if $DEBUG_RDOC - - convert_attrs(@str, @attrs) - convert_html(@str, @attrs) - convert_specials(str, @attrs) - - unmask_protected_sequences - - puts("After flow, str='#{@str.dump}'") if $DEBUG_RDOC - - return split_into_flow - end - - 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 - - def split_into_flow - display_attributes if $DEBUG_RDOC - - res = [] - current_attr = 0 - str = "" - - str_len = @str.length - - # skip leading invisible text - i = 0 - i += 1 while i < str_len and @str[i] == "\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 & Attribute::SPECIAL) != 0 - i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0 - res << 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] == "\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 - - return res - end - - end - end +require 'rdoc/markup/attribute_manager' diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index 3a500a4f0e..3c08d7bf6a 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -1,6 +1,7 @@ require 'rdoc/markup/formatter' require 'rdoc/markup/fragments' require 'rdoc/markup/inline' +require 'rdoc/generator' require 'cgi' @@ -47,7 +48,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter url = if path[0, 1] == '#' then # is this meaningful? path else - HTML.gen_url @from_path, path + RDoc::Generator.gen_url @from_path, path end end diff --git a/lib/rdoc/parsers/parse_rb.rb b/lib/rdoc/parsers/parse_rb.rb index 637789992d..64f49c97c2 100644 --- a/lib/rdoc/parsers/parse_rb.rb +++ b/lib/rdoc/parsers/parse_rb.rb @@ -2316,7 +2316,7 @@ class RDoc::RubyParser when "section" context.set_current_section(param, comment) - comment.clear + comment.replace '' break else diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb index 6b7de2f3b7..38f6374764 100644 --- a/lib/rdoc/rdoc.rb +++ b/lib/rdoc/rdoc.rb @@ -228,37 +228,37 @@ module RDoc def document(argv) TopLevel::reset - options = Options.new GENERATORS - options.parse argv + @options = Options.new GENERATORS + @options.parse argv @last_created = nil - unless options.all_one_file - @last_created = setup_output_dir(options.op_dir, options.force_update) + unless @options.all_one_file then + @last_created = setup_output_dir @options.op_dir, @options.force_update end start_time = Time.now - file_info = parse_files(options) + file_info = parse_files @options if file_info.empty? - $stderr.puts "\nNo newer files." unless options.quiet + $stderr.puts "\nNo newer files." unless @options.quiet else - gen = options.generator + gen = @options.generator - $stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet + $stderr.puts "\nGenerating #{gen.key.upcase}..." unless @options.quiet require gen.file_name gen_class = ::RDoc::Generator.const_get gen.class_name - gen = gen_class.for(options) + gen = gen_class.for @options pwd = Dir.pwd - Dir.chdir(options.op_dir) unless options.all_one_file + Dir.chdir @options.op_dir unless @options.all_one_file begin - Diagram.new(file_info, options).draw if options.diagram + Diagram.new(file_info, @options).draw if @options.diagram gen.generate(file_info) update_output_dir(".", start_time) ensure @@ -266,7 +266,7 @@ module RDoc end end - unless options.quiet + unless @options.quiet puts @stats.print end diff --git a/lib/rdoc/ri/descriptions.rb b/lib/rdoc/ri/descriptions.rb index 9887862e0a..adc3e7774b 100644 --- a/lib/rdoc/ri/descriptions.rb +++ b/lib/rdoc/ri/descriptions.rb @@ -13,6 +13,7 @@ class RDoc::RI::NamedThing def initialize(name) @name = name end + def <=>(other) @name <=> other.name end @@ -30,6 +31,7 @@ class RDoc::RI::AliasName < RDoc::RI::NamedThing; end class RDoc::RI::Attribute < RDoc::RI::NamedThing attr_reader :rw, :comment + def initialize(name, rw, comment) super(name) @rw = rw @@ -39,6 +41,7 @@ end class RDoc::RI::Constant < RDoc::RI::NamedThing attr_reader :value, :comment + def initialize(name, value, comment) super(name) @value = value diff --git a/lib/rdoc/ri/display.rb b/lib/rdoc/ri/display.rb index fa331c46c2..379cef11b3 100644 --- a/lib/rdoc/ri/display.rb +++ b/lib/rdoc/ri/display.rb @@ -2,9 +2,9 @@ require 'rdoc/ri' ## # This is a kind of 'flag' module. If you want to write your own 'ri' display -# module (perhaps because you'r writing an IDE or somesuch beast), you simply -# write a class which implements the various 'display' methods in -# 'DefaultDisplay', and include the 'RiDisplay' module in that class. +# module (perhaps because you're writing an IDE), you write a class which +# implements the various 'display' methods in RDoc::RI::DefaultDisplay, and +# include the RDoc::RI::Display module in that class. # # To access your class from the command line, you can do # @@ -32,26 +32,14 @@ class RDoc::RI::DefaultDisplay include RDoc::RI::Display - def initialize(formatter, width, use_stdout) + def initialize(formatter, width, use_stdout, output = $stdout) @use_stdout = use_stdout - @formatter = formatter.new $stdout, width, " " + @formatter = formatter.new output, width, " " end - def display_method_info(method) - page do - @formatter.draw_line(method.full_name) - display_params(method) - @formatter.draw_line - display_flow(method.comment) - if method.aliases && !method.aliases.empty? - @formatter.blankline - aka = "(also known as " - aka << method.aliases.map {|a| a.name }.join(", ") - aka << ")" - @formatter.wrap(aka) - end - end - end + ## + # Display information about +klass+. Fetches additional information from + # +ri_reader+ as necessary. def display_class_info(klass, ri_reader) page do @@ -90,89 +78,150 @@ class RDoc::RI::DefaultDisplay unless klass.constants.empty? @formatter.blankline @formatter.display_heading("Constants:", 2, "") - len = 0 - klass.constants.each { |c| len = c.name.length if c.name.length > len } - len += 2 - klass.constants.each do |c| - @formatter.wrap(c.value, - @formatter.indent+((c.name+":").ljust(len))) + + constants = klass.constants.sort_by { |constant| constant.name } + + constants.each do |constant| + if constant.comment then + @formatter.wrap "#{constant.name}:" + + @formatter.indent do + @formatter.display_flow constant.comment + end + else + @formatter.wrap constant.name + end end end - unless klass.class_methods.empty? - @formatter.blankline - @formatter.display_heading("Class methods:", 2, "") - @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', ')) - end + class_data = [ + :class_methods, + :class_method_extensions, + :instance_methods, + :instance_method_extensions, + ] - unless klass.class_method_extensions.empty? - @formatter.blankline - @formatter.display_heading("Class Method Extensions:", 2, "") - @formatter.wrap(klass.class_method_extensions.map{|m| m.name}.sort.join(', ')) - end + class_data.each do |data_type| + data = klass.send data_type - unless klass.instance_methods.empty? - @formatter.blankline - @formatter.display_heading("Instance methods:", 2, "") - @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', ')) + unless data.empty? then + @formatter.blankline + + heading = data_type.to_s.split('_').join(' ').capitalize << ':' + @formatter.display_heading heading, 2, '' + + data = data.map { |item| item.name }.sort.join ', ' + @formatter.wrap data + end end - unless klass.instance_method_extensions.empty? + unless klass.attributes.empty? then @formatter.blankline - @formatter.display_heading("Instance Method Extensions:", 2, "") - @formatter.wrap(klass.instance_method_extensions.map{|m| m.name}.sort.join(', ')) + + @formatter.display_heading 'Attributes:', 2, '' + + attributes = klass.attributes.sort_by { |attribute| attribute.name } + + attributes.each do |attribute| + if attribute.comment then + @formatter.wrap "#{attribute.name} (#{attribute.rw}):" + @formatter.indent do + @formatter.display_flow attribute.comment + end + else + @formatter.wrap "#{attribute.name} (#{attribute.rw})" + end + end end + end + end + + ## + # Display an Array of RDoc::Markup::Flow objects, +flow+. + + def display_flow(flow) + if flow and not flow.empty? then + @formatter.display_flow flow + else + @formatter.wrap '[no description]' + end + end + + ## + # Display information about +method+. - unless klass.attributes.empty? + def display_method_info(method) + page do + @formatter.draw_line(method.full_name) + display_params(method) + + @formatter.draw_line + display_flow(method.comment) + + if method.aliases and not method.aliases.empty? then @formatter.blankline - @formatter.wrap("Attributes:", "") - @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', ')) + aka = "(also known as #{method.aliases.map { |a| a.name }.join(', ')})" + @formatter.wrap aka end end end ## - # Display a list of method names + # Display the list of +methods+. def display_method_list(methods) page do - @formatter.raw_print_line("More than one method matched your request. You can refine") - @formatter.raw_print_line("your search by asking for information on one of:\n\n") - @formatter.wrap(methods.map {|m| m.full_name} .join(", ")) + @formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:" + + @formatter.blankline + + @formatter.wrap methods.map { |m| m.full_name }.join(", ") end end - def display_class_list(namespaces) - page do - @formatter.raw_print_line("More than one class or module matched your request. You can refine") - @formatter.raw_print_line("your search by asking for information on one of:\n\n") - @formatter.wrap(namespaces.map {|m| m.full_name}.join(", ")) + ## + # Display the params for +method+. + + def display_params(method) + params = method.params + + if params[0,1] == "(" then + if method.is_singleton + params = method.full_name + params + else + params = method.name + params + end + end + + params.split(/\n/).each do |param| + @formatter.wrap param + @formatter.break_to_newline + end + + if method.source_path then + @formatter.blankline + @formatter.wrap("Extension from #{method.source_path}") end end + ## + # List the classes in +classes+. + def list_known_classes(classes) if classes.empty? warn_no_database else page do - @formatter.draw_line("Known classes and modules") + @formatter.draw_line "Known classes and modules" @formatter.blankline - @formatter.wrap(classes.sort.join(", ")) - end - end - end - def list_known_names(names) - if names.empty? - warn_no_database - else - page do - names.each {|n| @formatter.raw_print_line(n)} + @formatter.wrap classes.sort.join(', ') end end end - private + ## + # Paginates output through a pager program. def page if pager = setup_pager then @@ -190,6 +239,9 @@ class RDoc::RI::DefaultDisplay rescue Errno::EPIPE end + ## + # Sets up a pager program to pass output through. + def setup_pager unless @use_stdout then for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq @@ -200,45 +252,23 @@ class RDoc::RI::DefaultDisplay end end - def display_params(method) - params = method.params - - if params[0,1] == "(" - if method.is_singleton - params = method.full_name + params - else - params = method.name + params - end - end - params.split(/\n/).each do |p| - @formatter.wrap(p) - @formatter.break_to_newline - end - if method.source_path then - @formatter.blankline - @formatter.wrap("Extension from #{method.source_path}") - end - end - - def display_flow(flow) - if !flow || flow.empty? - @formatter.wrap("(no description...)") - else - @formatter.display_flow(flow) - end - end + ## + # Displays a message that describes how to build RI data. def warn_no_database - puts "No ri data found" - puts - puts "If you've installed Ruby yourself, you need to generate documentation using:" - puts - puts " make install-doc" - puts - puts "from the same place you ran `make` to build ruby." - puts - puts "If you installed Ruby from a packaging system, then you may need to" - puts "install an additional package, or ask the packager to enable ri generation." + output = @formatter.output + + output.puts "No ri data found" + output.puts + output.puts "If you've installed Ruby yourself, you need to generate documentation using:" + output.puts + output.puts " make install-doc" + output.puts + output.puts "from the same place you ran `make` to build ruby." + output.puts + output.puts "If you installed Ruby from a packaging system, then you may need to" + output.puts "install an additional package, or ask the packager to enable ri generation." end + end diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index b254d3574f..7ac2e0f15c 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -344,7 +344,11 @@ Options may also be set in the 'RI' environment variable. end def read_yaml(path) - YAML.load File.read(path).gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI|SM).*/, '') + data = File.read path + data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '') + data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/, + ' !ruby/\1:RDoc::Markup::\2') + YAML.load data end def get_info_for(arg) @@ -418,7 +422,7 @@ Options may also be set in the 'RI' environment variable. end -class Hash +class Hash # HACK don't add stuff to Hash. def method_missing method, *args self[method.to_s] end @@ -428,7 +432,12 @@ class Hash if self[k] then case v when Array then - self[k] += v + # HACK dunno + if String === self[k] and self[k].empty? then + self[k] = v + else + self[k] += v + end when Hash then self[k].merge! v else diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb index df73bf5eb3..0a0c3f7380 100644 --- a/lib/rdoc/ri/formatter.rb +++ b/lib/rdoc/ri/formatter.rb @@ -3,7 +3,7 @@ require 'rdoc/markup' class RDoc::RI::Formatter - attr_reader :indent + attr_writer :indent attr_accessor :output FORMATTERS = { } @@ -20,6 +20,7 @@ class RDoc::RI::Formatter @output = output @width = width @indent = indent + @original_indent = indent.dup end def draw_line(label=nil) @@ -42,6 +43,18 @@ class RDoc::RI::Formatter end end + def indent + return @indent unless block_given? + + begin + indent = @indent.dup + @indent += @original_indent + yield + ensure + @indent = indent + end + end + def wrap(txt, prefix=@indent, linelen=@width) return unless txt && !txt.empty? @@ -481,13 +494,13 @@ class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter when :LABELED then list_type = "dl" prefixer = proc do |li| - "<dt><b>" + escape(li.label) + "</b><dd>" + "<dt><b>" + escape(li.label) + "</b><dd>" end when :NOTE then list_type = "table" prefixer = proc do |li| - %{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>} + %{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>} end else fail "unknown list type" diff --git a/lib/rdoc/template.rb b/lib/rdoc/template.rb index aa35927c8b..53d0e3ce68 100644 --- a/lib/rdoc/template.rb +++ b/lib/rdoc/template.rb @@ -1,10 +1,12 @@ require 'erb' +module RDoc; end + ## -# An ERB wrapper. +# An ERb wrapper that allows nesting of one ERb template inside another. # # This TemplatePage operates similarly to RDoc 1.x's TemplatePage, but uses -# ERB instead of a custom template language. +# ERb instead of a custom template language. # # Converting from a RDoc 1.x template to an RDoc 2.x template is fairly easy. # @@ -24,8 +26,6 @@ require 'erb' # # So you can see what is being used inside which loop. -module RDoc -end class RDoc::TemplatePage ## |