1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Import RDoc 2.5

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27147 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2010-04-01 07:45:16 +00:00
parent 9b40cdfe8c
commit 46580b5147
176 changed files with 17841 additions and 16457 deletions

View file

@ -1,3 +1,7 @@
Thu Apr 1 16:44:00 2010 Eric Hodel <drbrain@segment7.net>
* lib/rdoc, test/rdoc: Imported RDoc 2.5
Thu Apr 1 14:30:16 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
* enc/utf_16{be,le}.c (utf16{be,le}_mbc_to_code): simplified.

View file

@ -13,10 +13,23 @@ require 'rdoc/rdoc'
begin
r = RDoc::RDoc.new
r.document ARGV
rescue Interrupt
$stderr.puts
$stderr.puts "Interrupted"
rescue RDoc::Error => e
$stderr.puts e.message
rescue SystemExit
raise
rescue Exception => e
if $DEBUG_RDOC then
$stderr.puts e.message
$stderr.puts "#{e.backtrace.join "\n\t"}"
$stderr.puts
elsif Interrupt === e then
$stderr.puts
$stderr.puts 'Interrupted'
else
$stderr.puts "uh-oh! RDoc had a problem:"
$stderr.puts e.message
$stderr.puts
$stderr.puts "run with --debug for full backtrace"
end
exit 1
end

View file

@ -1,5 +1,7 @@
$DEBUG_RDOC = nil
# :main: README.txt
##
# = \RDoc - Ruby Documentation System
#
@ -17,7 +19,8 @@ $DEBUG_RDOC = nil
#
# * 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::Parser::C
# * If you want to generate documentation for extensions written in C, see
# RDoc::Parser::C
# * If you want to drive RDoc programmatically, see RDoc::RDoc.
# * If you want to use the library to format text blocks into HTML, have a look
# at RDoc::Markup.
@ -31,6 +34,7 @@ $DEBUG_RDOC = nil
# % rdoc [options] [names...]
#
# For an up-to-date option summary, type
#
# % rdoc --help
#
# A typical use might be to generate documentation for a package of Ruby
@ -59,6 +63,7 @@ $DEBUG_RDOC = nil
# recursively for C and Ruby source files only.
#
# == \Options
#
# rdoc can be passed a variety of command-line options. In addition,
# options can be specified via the +RDOCOPT+ environment variable, which
# functions similarly to the +RUBYOPT+ environment variable.
@ -68,35 +73,22 @@ $DEBUG_RDOC = nil
# will make rdoc default to inline method source code. Command-line options
# always will override those in +RDOCOPT+.
#
# Run
# Run:
#
# % rdoc --help
# rdoc --help
#
# for full details on rdoc's options.
#
# Here are some of the most commonly used options.
# [-d, --diagram]
# Generate diagrams showing modules and
# classes. You need dot V1.8.6 or later to
# use the --diagram option correctly. Dot is
# available from http://graphviz.org
#
# [-S, --inline-source]
# Show method source code inline, rather than via a popup link.
#
# [-T, --template=NAME]
# Set the template used when generating output.
#
# == Documenting Source Code
#
# Comment blocks can be written fairly naturally, either using +#+ on
# Comment blocks can be written fairly naturally, either using <tt>#</tt> on
# successive lines of the comment, or by including the comment in
# a =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
#
@ -112,7 +104,7 @@ $DEBUG_RDOC = nil
# # FIXME: fails if the birthday falls on February 29th
# #++
# # The DOB is returned as a Time object.
#
#
# def get_dob(person)
# # ...
# end
@ -138,7 +130,7 @@ $DEBUG_RDOC = nil
#
# def fred # :yields: index, position
# # ...
#
#
# yield line, address
#
# which will get documented as
@ -148,6 +140,11 @@ $DEBUG_RDOC = nil
# +:yields:+ is an example of a documentation directive. These appear
# immediately after the start of the document element they are modifying.
#
# RDoc automatically cross-references words with underscores or camel-case.
# To suppress cross-references, prefix the word with a \\ character. To
# include special characters like "\\n", you'll need to use two \\
# characters like "\\\\\\n".
#
# == \Markup
#
# * The markup engine looks for a document's natural left margin. This is
@ -214,17 +211,17 @@ $DEBUG_RDOC = nil
#
# Word-based markup uses flag characters around individual words:
#
# [\*word*] displays word in a *bold* font
# [\_word_] displays word in an _emphasized_ font
# [\+word+] displays word in a +code+ font
# [<tt>\*word*</tt>] displays word in a *bold* font
# [<tt>\_word_</tt>] displays word in an _emphasized_ font
# [<tt>\+word+</tt>] displays word in a +code+ font
#
# General markup affects text between a start delimiter and and end
# delimiter. Not surprisingly, these delimiters look like HTML markup.
#
# [\<b>text...</b>] displays word in a *bold* font
# [\<em>text...</em>] displays word in an _emphasized_ font
# [\\<i>text...</i>] displays word in an <i>italicized</i> font
# [\<tt>text...</tt>] displays word in a +code+ font
# [<tt>\<b>text...</b></tt>] displays word in a *bold* font
# [<tt>\<em>text...</em></tt>] displays word in an _emphasized_ font
# [<tt>\<i>text...</i></tt>] displays word in an <i>italicized</i> font
# [<tt>\<tt>text...\</tt></tt>] displays word in a +code+ font
#
# Unlike conventional Wiki markup, general markup can cross line
# boundaries. You can turn off the interpretation of markup by
@ -233,7 +230,7 @@ $DEBUG_RDOC = nil
#
# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
# recognized. An HTTP url that references an external image file is
# converted into an inline <IMG..>. Hyperlinks starting 'link:' are
# converted into an inline \<IMG..>. Hyperlinks starting 'link:' are
# assumed to refer to local files whose path is relative to the --op
# directory.
#
@ -242,6 +239,14 @@ $DEBUG_RDOC = nil
# used as the target. If +label+ contains multiple words,
# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
#
# Example hyperlinks:
#
# link:RDoc.html
# http://rdoc.rubyforge.org
# mailto:user@example.com
# {RDoc Documentation}[http://rdoc.rubyforge.org]
# {RDoc Markup}[link:RDoc/Markup.html]
#
# == Directives
#
# [+:nodoc:+ / +:nodoc:+ all]
@ -251,12 +256,12 @@ $DEBUG_RDOC = nil
# module also will 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
@ -290,7 +295,7 @@ $DEBUG_RDOC = nil
# 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.
@ -327,6 +332,8 @@ $DEBUG_RDOC = nil
# last. If you don't specify a +:startdoc:+ by the end of the container,
# disables documentation for the entire class or module.
#
# Further directives can be found in RDoc::Parser::Ruby and RDoc::Parser::C
#
# == Other stuff
#
# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>
@ -339,9 +346,6 @@ $DEBUG_RDOC = nil
# 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.
@ -367,12 +371,19 @@ module RDoc
class Error < RuntimeError; end
RDocError = Error # :nodoc:
def self.const_missing const_name # :nodoc:
if const_name.to_s == 'RDocError' then
warn "RDoc::RDocError is deprecated"
return Error
end
super
end
##
# RDoc version you are using
VERSION = "2.2.2"
VERSION = '2.5'
##
# Name of the dotfile that contains the description of files to be processed
@ -380,14 +391,29 @@ module RDoc
DOT_DOC_FILENAME = ".document"
##
# General RDoc modifiers
GENERAL_MODIFIERS = %w[nodoc].freeze
##
# RDoc modifiers for classes
CLASS_MODIFIERS = GENERAL_MODIFIERS
ATTR_MODIFIERS = GENERAL_MODIFIERS
##
# RDoc modifiers for attributes
ATTR_MODIFIERS = GENERAL_MODIFIERS
##
# RDoc modifiers for constants
CONSTANT_MODIFIERS = GENERAL_MODIFIERS
##
# RDoc modifiers for methods
METHOD_MODIFIERS = GENERAL_MODIFIERS +
%w[arg args yield yields notnew not-new not_new doc]

View file

@ -1,232 +0,0 @@
= 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 programmatically, 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 '+#--+'.
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 '+#+++'.
##
# 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.
[+: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:
[+:section: title+]
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 --title 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 --main 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 specify a +:startdoc:+ by the end of the container,
disables documentation for the entire class or module.
= Other stuff
Author:: Dave Thomas <dave@pragmaticprogrammer.com>
== 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.

54
lib/rdoc/alias.rb Normal file
View file

@ -0,0 +1,54 @@
require 'rdoc/code_object'
##
# Represent an alias, which is an old_name/new_name pair associated with a
# particular context
class RDoc::Alias < RDoc::CodeObject
##
# Allow comments to be overridden
attr_writer :comment
##
# Aliased name
attr_accessor :new_name
##
# Aliasee's name
attr_accessor :old_name
##
# Source file token stream
attr_accessor :text
##
# Creates a new Alias with a token stream of +text+ that aliases +old_name+
# to +new_name+ and has +comment+
def initialize(text, old_name, new_name, comment)
super()
@text = text
@old_name = old_name
@new_name = new_name
self.comment = comment
end
def inspect # :nodoc:
parent_name = parent ? parent.name : '(unknown)'
"#<%s:0x%x %s.alias_method %s, %s>" % [
self.class, object_id,
parent_name, @old_name, @new_name,
]
end
def to_s # :nodoc:
"alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
end
end

10
lib/rdoc/anon_class.rb Normal file
View file

@ -0,0 +1,10 @@
require 'rdoc/class_module'
##
# An anonymous class like:
#
# c = Class.new do end
class RDoc::AnonClass < RDoc::ClassModule
end

282
lib/rdoc/any_method.rb Normal file
View file

@ -0,0 +1,282 @@
require 'rdoc/code_object'
require 'rdoc/tokenstream'
##
# AnyMethod is the base class for objects representing methods
class RDoc::AnyMethod < RDoc::CodeObject
MARSHAL_VERSION = 0 # :nodoc:
include Comparable
##
# Method name
attr_writer :name
##
# public, protected, private
attr_accessor :visibility
##
# Parameters yielded by the called block
attr_accessor :block_params
##
# Don't rename \#initialize to \::new
attr_accessor :dont_rename_initialize
##
# Is this a singleton method?
attr_accessor :singleton
##
# Source file token stream
attr_reader :text
##
# Array of other names for this method
attr_reader :aliases
##
# Fragment reference for this method
attr_reader :aref
##
# The method we're aliasing
attr_accessor :is_alias_for
##
# Parameters for this method
attr_overridable :params, :param, :parameters, :parameter
##
# Different ways to call this method
attr_accessor :call_seq
include RDoc::TokenStream
##
# Resets method fragment reference counter
def self.reset
@@aref = 'M000000'
end
reset
def initialize(text, name)
super()
@text = text
@name = name
@aliases = []
@block_params = nil
@call_seq = nil
@dont_rename_initialize = false
@is_alias_for = nil
@parent_name = nil
@singleton = nil
@token_stream = nil
@visibility = :public
@aref = @@aref
@@aref = @@aref.succ
end
##
# Order by #singleton then #name
def <=>(other)
[@singleton ? 0 : 1, @name] <=> [other.singleton ? 0 : 1, other.name]
end
##
# Adds +method+ as an alias for this method
def add_alias(method)
@aliases << method
end
##
# HTML id-friendly method name
def html_name
@name.gsub(/[^a-z]+/, '-')
end
def inspect # :nodoc:
alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
"#<%s:0x%x %s (%s)%s>" % [
self.class, object_id,
full_name,
visibility,
alias_for,
]
end
##
# Full method name including namespace
def full_name
@full_name ||= "#{@parent ? @parent.full_name : '(unknown)'}#{pretty_name}"
end
##
# Dumps this AnyMethod for use by ri. See also #marshal_load
def marshal_dump
aliases = @aliases.map do |a|
[a.full_name, parse(a.comment)]
end
[ MARSHAL_VERSION,
@name,
full_name,
@singleton,
@visibility,
parse(@comment),
@call_seq,
@block_params,
aliases,
]
end
##
# Loads this AnyMethod from +array+. For a loaded AnyMethod the following
# methods will return cached values:
#
# * #full_name
# * #parent_name
def marshal_load(array)
@aliases = []
@dont_rename_initialize = nil
@is_alias_for = nil
@token_stream = nil
@name = array[1]
@full_name = array[2]
@singleton = array[3]
@visibility = array[4]
@comment = array[5]
@call_seq = array[6]
@block_params = array[7]
@parent_name = if @full_name =~ /#/ then
$`
else
name = @full_name.split('::')
name.pop
name.join '::'
end
array[8].each do |old_name, new_name, comment|
add_alias RDoc::Alias.new(nil, old_name, new_name, comment)
end
end
##
# Method name
def name
return @name if @name
@name = @call_seq[/^.*?\.(\w+)/, 1] || @call_seq if @call_seq
end
##
# Pretty parameter list for this method
def param_seq
params = params.gsub(/\s*\#.*/, '')
params = params.tr("\n", " ").squeeze(" ")
params = "(#{params})" unless p[0] == ?(
if block = block_params then # yes, =
# If this method has explicit block parameters, remove any explicit
# &block
params.sub!(/,?\s*&\w+/)
block.gsub!(/\s*\#.*/, '')
block = block.tr("\n", " ").squeeze(" ")
if block[0] == ?(
block.sub!(/^\(/, '').sub!(/\)/, '')
end
params << " { |#{block}| ... }"
end
params
end
##
# Name of our parent with special handling for un-marshaled methods
def parent_name
@parent_name || super
end
##
# Path to this method
def path
"#{@parent.path}##{@aref}"
end
##
# Method name with class/instance indicator
def pretty_name
"#{singleton ? '::' : '#'}#{@name}"
end
def pretty_print q # :nodoc:
alias_for = @is_alias_for ? "alias for #{@is_alias_for.name}" : nil
q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
if alias_for then
q.breakable
q.text alias_for
end
if text then
q.breakable
q.text "text:"
q.breakable
q.pp @text
end
unless comment.empty? then
q.breakable
q.text "comment:"
q.breakable
q.pp @comment
end
end
end
def to_s # :nodoc:
"#{self.class.name}: #{full_name} (#{@text})\n#{@comment}"
end
##
# Type of method (class or instance)
def type
singleton ? 'class' : 'instance'
end
end

165
lib/rdoc/attr.rb Normal file
View file

@ -0,0 +1,165 @@
require 'rdoc/code_object'
##
# An attribute created by \#attr, \#attr_reader, \#attr_writer or
# \#attr_accessor
class RDoc::Attr < RDoc::CodeObject
MARSHAL_VERSION = 0 # :nodoc:
##
# Name of the attribute
attr_accessor :name
##
# Is the attribute readable, writable or both?
attr_accessor :rw
##
# Source file token stream
attr_accessor :text
##
# public, protected, private
attr_accessor :visibility
def initialize(text, name, rw, comment)
super()
@text = text
@name = name
@rw = rw
@visibility = :public
self.comment = comment
end
##
# Attributes are ordered by name
def <=>(other)
self.name <=> other.name
end
##
# Attributes are equal when their names and rw is identical
def == other
self.class == other.class and
self.name == other.name and
self.rw == other.rw
end
##
# Returns nil, for duck typing with RDoc::AnyMethod
def block_params
end
##
# Returns nil, for duck typing with RDoc::AnyMethod
def call_seq
end
##
# Partially bogus as Attr has no parent. For duck typing with
# RDoc::AnyMethod.
def full_name
@full_name ||= "#{@parent ? @parent.full_name : '(unknown)'}##{name}"
end
##
# An HTML id-friendly representation of #name
def html_name
@name.gsub(/[^a-z]+/, '-')
end
def inspect # :nodoc:
attr = case rw
when 'RW' then :attr_accessor
when 'R' then :attr_reader
when 'W' then :attr_writer
else
" (#{rw})"
end
"#<%s:0x%x %s.%s :%s>" % [
self.class, object_id,
parent_name, attr, @name,
]
end
##
# Dumps this Attr for use by ri. See also #marshal_load
def marshal_dump
[ MARSHAL_VERSION,
@name,
full_name,
@rw,
@visibility,
parse(@comment),
]
end
##
# Loads this AnyMethod from +array+. For a loaded AnyMethod the following
# methods will return cached values:
#
# * #full_name
# * #parent_name
def marshal_load array
@name = array[1]
@full_name = array[2]
@rw = array[3]
@visibility = array[4]
@comment = array[5]
@parent_name = @full_name
end
##
# Name of our parent with special handling for un-marshaled methods
def parent_name
@parent_name || super
end
##
# URL path for this attribute
def path
"#{@parent.path}##{@name}"
end
##
# For duck typing with RDoc::AnyMethod
def singleton
false
end
def to_s # :nodoc:
"#{type} #{name}\n#{comment}"
end
##
# Returns attr_reader, attr_writer or attr_accessor as appropriate
def type
case @rw
when 'RW' then 'attr_accessor'
when 'R' then 'attr_reader'
when 'W' then 'attr_writer'
end
end
end

213
lib/rdoc/class_module.rb Normal file
View file

@ -0,0 +1,213 @@
require 'rdoc/context'
##
# ClassModule is the base class for objects representing either a class or a
# module.
class RDoc::ClassModule < RDoc::Context
MARSHAL_VERSION = 0 # :nodoc:
attr_accessor :diagram
##
# Creates a new ClassModule with +name+ with optional +superclass+
def initialize(name, superclass = 'Object')
@diagram = nil
@full_name = nil
@name = name
@superclass = superclass
super()
end
##
# Ancestors list for this ClassModule (abstract)
def ancestors
raise NotImplementedError
end
##
# Appends +comment+ to the current comment, but separated by a rule. Works
# more like <tt>+=</tt>.
def comment=(comment)
return if comment.empty?
comment = "#{@comment}\n---\n#{normalize_comment comment}" unless
@comment.empty?
super
end
##
# Finds a class or module with +name+ in this namespace or its descendents
def find_class_named(name)
return self if full_name == name
return self if @name == name
@classes.values.find do |klass|
next if klass == self
klass.find_class_named name
end
end
##
# Return the fully qualified name of this class or module
def full_name
@full_name ||= if RDoc::ClassModule === @parent then
"#{@parent.full_name}::#{@name}"
else
@name
end
end
##
# 'module' or 'class'
def type
module? ? 'module' : 'class'
end
def marshal_dump # :nodoc:
attrs = attributes.sort.map do |attr|
[attr.name, attr.rw]
end
method_types = methods_by_type.map do |type, visibilities|
visibilities = visibilities.map do |visibility, methods|
method_names = methods.map do |method|
method.name
end
[visibility, method_names.uniq]
end
[type, visibilities]
end
[ MARSHAL_VERSION,
@name,
full_name,
@superclass,
parse(@comment),
attrs,
constants.map do |const|
[const.name, parse(const.comment)]
end,
includes.map do |incl|
[incl.name, parse(incl.comment)]
end,
method_types,
]
end
def marshal_load array # :nodoc:
initialize_methods_etc
@document_self = true
@done_documenting = false
@current_section = nil
@name = array[1]
@full_name = array[2]
@superclass = array[3]
@comment = array[4]
array[5].each do |name, rw|
add_attribute RDoc::Attr.new(nil, name, rw, nil)
end
array[6].each do |name, comment|
add_constant RDoc::Constant.new(name, nil, comment)
end
array[7].each do |name, comment|
add_include RDoc::Include.new(name, comment)
end
array[8].each do |type, visibilities|
visibilities.each do |visibility, methods|
@visibility = visibility
methods.each do |name|
method = RDoc::AnyMethod.new nil, name
method.singleton = true if type == 'class'
add_method method
end
end
end
end
##
# Merges +class_module+ into this ClassModule
def merge class_module
if class_module.comment then
document = parse @comment
class_module.comment.parts.push(*document.parts)
@comment = class_module.comment
end
class_module.each_attribute do |attr|
if match = attributes.find { |a| a.name == attr.name } then
match.rw = [match.rw, attr.rw].compact.join
else
add_attribute attr
end
end
class_module.each_constant do |const|
add_constant const
end
class_module.each_include do |incl|
add_include incl
end
class_module.each_method do |meth|
add_method meth
end
end
##
# Does this object represent a module?
def module?
false
end
##
# Path to this class or module
def path
http_url RDoc::RDoc.current.generator.class_dir
end
##
# Get the superclass of this class. Attempts to retrieve the superclass
# object, returns the name if it is not known.
def superclass
RDoc::TopLevel.find_class_named_from(@superclass, parent) || @superclass
end
##
# Set the superclass of this class to +superclass+
def superclass=(superclass)
raise NoMethodError, "#{full_name} is a module" if module?
@superclass = superclass if @superclass.nil? or @superclass == 'Object'
end
def to_s # :nodoc:
"#{self.class}: #{full_name} #{@comment} #{super}"
end
end

175
lib/rdoc/code_object.rb Normal file
View file

@ -0,0 +1,175 @@
require 'rdoc'
require 'rdoc/text'
##
# Base class for the RDoc code tree.
#
# We contain the common stuff for contexts (which are containers) and other
# elements (methods, attributes and so on)
class RDoc::CodeObject
include RDoc::Text
##
# Our comment
attr_reader :comment
##
# Do we document our children?
attr_reader :document_children
##
# Do we document ourselves?
attr_reader :document_self
##
# Are we done documenting (ie, did we come across a :enddoc:)?
attr_accessor :done_documenting
##
# Force documentation of this CodeObject
attr_accessor :force_documentation
##
# Our parent CodeObject
attr_accessor :parent
##
# Which section are we in
attr_accessor :section
##
# We are the model of the code, but we know that at some point we will be
# worked on by viewers. By implementing the Viewable protocol, viewers can
# associated themselves with these objects.
attr_accessor :viewer
##
# There's a wee trick we pull. Comment blocks can have directives that
# override the stuff we extract during the parse. So, we have a special
# class method, attr_overridable, that lets code objects list those
# directives. When a comment is assigned, we then extract out any matching
# directives and update our object
def self.attr_overridable(name, *aliases)
@overridables ||= {}
attr_accessor name
aliases.unshift name
aliases.each do |directive_name|
@overridables[directive_name.to_s] = name
end
end
##
# Creates a new CodeObject that will document itself and its children
def initialize
@comment = ''
@document_children = true
@document_self = true
@done_documenting = false
@force_documentation = false
@parent = nil
end
##
# Replaces our comment with +comment+, unless it is empty.
def comment=(comment)
@comment = case comment
when NilClass then ''
when RDoc::Markup::Document then comment
else
if comment and not comment.empty? then
normalize_comment comment
else
@comment
end
end
end
##
# Enables or disables documentation of this CodeObject's children. Calls
# remove_classes_and_modules when disabling.
def document_children=(document_children)
@document_children = document_children
remove_classes_and_modules unless document_children
end
##
# Enables or disables documentation of this CodeObject. Calls
# remove_methods_etc when disabling.
def document_self=(document_self)
@document_self = document_self
remove_methods_etc unless document_self
end
##
# Does this class have a comment with content or is document_self false.
def documented?
!(@document_self and @comment.empty?)
end
##
# File name of our parent
def parent_file_name
@parent ? @parent.base_name : '(unknown)'
end
##
# Name of our parent
def parent_name
@parent ? @parent.full_name : '(unknown)'
end
##
# Callback called upon disabling documentation of children. See
# #document_children=
def remove_classes_and_modules
end
##
# Callback called upon disabling documentation of ourself. See
# #document_self=
def remove_methods_etc
end
##
# Enable capture of documentation
def start_doc
@document_self = true
@document_children = true
end
##
# Disable capture of documentation
def stop_doc
@document_self = false
@document_children = false
end
end

File diff suppressed because it is too large Load diff

58
lib/rdoc/constant.rb Normal file
View file

@ -0,0 +1,58 @@
require 'rdoc/code_object'
##
# A constant
class RDoc::Constant < RDoc::CodeObject
##
# The constant's name
attr_accessor :name
##
# The constant's value
attr_accessor :value
##
# Creates a new constant with +name+, +value+ and +comment+
def initialize(name, value, comment)
super()
@name = name
@value = value
self.comment = comment
end
##
# Constants are ordered by name
def <=> other
return unless self.class === other
[parent_name, name] <=> [other.parent_name, other.name]
end
def == other
self.class == other.class and
@parent == other.parent and
@name == other.name
end
def inspect # :nodoc:
"#<%s:0x%x %s::%s>" % [
self.class, object_id,
parent_name, @name,
]
end
##
# Path to this constant
def path
"#{@parent.path}##{@name}"
end
end

755
lib/rdoc/context.rb Normal file
View file

@ -0,0 +1,755 @@
require 'rdoc/code_object'
##
# A Context is something that can hold modules, classes, methods, attributes,
# aliases, requires, and includes. Classes, modules, and files are all
# Contexts.
class RDoc::Context < RDoc::CodeObject
include Comparable
##
# Types of methods
TYPES = %w[class instance]
##
# Method visibilities
VISIBILITIES = [:public, :protected, :private]
##
# Aliased methods
attr_reader :aliases
##
# attr* methods
attr_reader :attributes
##
# Constants defined
attr_reader :constants
##
# Current section of documentation
attr_reader :current_section
##
# Files this context is found in
attr_reader :in_files
##
# Modules this context includes
attr_reader :includes
##
# Methods defined in this context
attr_reader :method_list
##
# Name of this class excluding namespace. See also full_name
attr_reader :name
##
# Files this context requires
attr_reader :requires
##
# Sections in this context
attr_reader :sections
##
# Aliases that haven't been resolved to a method
attr_accessor :unmatched_alias_lists
##
# Current visibility of this context
attr_reader :visibility
##
# A per-comment section of documentation like:
#
# # :SECTION: The title
# # The body
class Section
##
# Section comment
attr_reader :comment
##
# Context this Section lives in
attr_reader :parent
##
# Section sequence number (for linking)
attr_reader :sequence
##
# Section title
attr_reader :title
@@sequence = "SEC00000"
##
# Creates a new section with +title+ and +comment+
def initialize(parent, title, comment)
@parent = parent
@title = title
@@sequence.succ!
@sequence = @@sequence.dup
set_comment comment
end
##
# Sections are equal when they have the same #sequence
def ==(other)
self.class === other and @sequence == other.sequence
end
def inspect # :nodoc:
"#<%s:0x%x %s %p>" % [
self.class, object_id,
@sequence, title
]
end
##
# Set the comment for this section from the original comment block If
# the first line contains :section:, strip it and use the rest.
# Otherwise remove lines up to the line containing :section:, and look
# for those lines again at the end and remove them. This lets us write
#
# # blah blah blah
# #
# # :SECTION: The title
# # The body
def set_comment(comment)
return unless comment
if comment =~ /^#[ \t]*:section:.*\n/ then
start = $`
rest = $'
if start.empty?
@comment = rest
else
@comment = rest.sub(/#{start.chomp}\Z/, '')
end
else
@comment = comment
end
@comment = nil if @comment.empty?
end
end
##
# Creates an unnamed empty context with public visibility
def initialize
super
@in_files = []
@name ||= "unknown"
@comment ||= ""
@parent = nil
@visibility = :public
@current_section = Section.new self, nil, nil
@sections = [@current_section]
initialize_methods_etc
initialize_classes_and_modules
end
##
# Sets the defaults for classes and modules
def initialize_classes_and_modules
@classes = {}
@modules = {}
end
##
# Sets the defaults for methods and so-forth
def initialize_methods_etc
@method_list = []
@attributes = []
@aliases = []
@requires = []
@includes = []
@constants = []
# This Hash maps a method name to a list of unmatched aliases (aliases of
# a method not yet encountered).
@unmatched_alias_lists = {}
end
##
# Contexts are sorted by full_name
def <=>(other)
full_name <=> other.full_name
end
##
# Adds +an_alias+ that is automatically resolved
def add_alias(an_alias)
meth = find_instance_method_named(an_alias.old_name)
if meth then
add_alias_impl an_alias, meth
else
add_to @aliases, an_alias
unmatched_alias_list = @unmatched_alias_lists[an_alias.old_name] ||= []
unmatched_alias_list.push an_alias
end
an_alias
end
##
# Turns +an_alias+ into an AnyMethod that points to +meth+
def add_alias_impl(an_alias, meth)
new_meth = RDoc::AnyMethod.new an_alias.text, an_alias.new_name
new_meth.is_alias_for = meth
new_meth.singleton = meth.singleton
new_meth.params = meth.params
new_meth.comment = an_alias.comment
meth.add_alias new_meth
add_method new_meth
# aliases don't use ongoing visibility
new_meth.visibility = meth.visibility
new_meth
end
##
# Adds +attribute+
def add_attribute(attribute)
add_to @attributes, attribute
end
##
# Adds a class named +name+ with +superclass+.
#
# Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module
# unless it later sees <tt>class Container</tt>. add_class automatically
# upgrades +name+ to a class in this case.
def add_class(class_type, name, superclass = 'Object')
klass = add_class_or_module @classes, class_type, name, superclass
existing = klass.superclass
existing = existing.name if existing and not String === existing
if superclass != existing and superclass != 'Object' then
klass.superclass = superclass
end
# If the parser encounters Container::Item before encountering
# Container, then it assumes that Container is a module. This may not
# be the case, so remove Container from the module list if present and
# transfer any contained classes and modules to the new class.
mod = RDoc::TopLevel.modules_hash.delete klass.full_name
if mod then
klass.classes_hash.update mod.classes_hash
klass.modules_hash.update mod.modules_hash
klass.method_list.concat mod.method_list
@modules.delete klass.name
end
RDoc::TopLevel.classes_hash[klass.full_name] = klass
klass
end
##
# Instantiates a +class_type+ named +name+ and adds it the modules or
# classes Hash +collection+.
def add_class_or_module(collection, class_type, name, superclass = nil)
full_name = child_name name
mod = collection[name]
if mod then
mod.superclass = superclass unless mod.module?
else
all = if class_type == RDoc::NormalModule then
RDoc::TopLevel.modules_hash
else
RDoc::TopLevel.classes_hash
end
mod = all[full_name]
unless mod then
mod = class_type.new name, superclass
else
# If the class has been encountered already, check that its
# superclass has been set (it may not have been, depending on the
# context in which it was encountered).
if class_type == RDoc::NormalClass then
mod.superclass = superclass unless mod.superclass
end
end
unless @done_documenting then
all[full_name] = mod
collection[name] = mod
end
mod.section = @current_section
mod.parent = self
end
mod
end
##
# Adds +constant+
def add_constant(constant)
add_to @constants, constant
end
##
# Adds included module +include+
def add_include(include)
add_to @includes, include
end
##
# Adds +method+
def add_method(method)
method.visibility = @visibility
add_to @method_list, method
unmatched_alias_list = @unmatched_alias_lists[method.name]
if unmatched_alias_list then
unmatched_alias_list.each do |unmatched_alias|
add_alias_impl unmatched_alias, method
@aliases.delete unmatched_alias
end
@unmatched_alias_lists.delete method.name
end
end
##
# Adds a module named +name+. If RDoc already knows +name+ is a class then
# that class is returned instead. See also #add_class
def add_module(class_type, name)
return @classes[name] if @classes.key? name
add_class_or_module @modules, class_type, name, nil
end
##
# Adds an alias from +from+ to +name+
def add_module_alias from, name
to_name = child_name name
unless @done_documenting then
if from.module? then
RDoc::TopLevel.modules_hash
else
RDoc::TopLevel.classes_hash
end[to_name] = from
if from.module? then
@modules
else
@classes
end[name] = from
end
from
end
##
# Adds +require+ to this context's top level
def add_require(require)
if RDoc::TopLevel === self then
add_to @requires, require
else
parent.add_require require
end
end
##
# Adds +thing+ to the collection +array+
def add_to(array, thing)
array << thing if @document_self and not @done_documenting
thing.parent = self
thing.section = @current_section
end
##
# Creates the full name for a child with +name+
def child_name name
if RDoc::TopLevel === self then
name
else
"#{self.full_name}::#{name}"
end
end
##
# Array of classes in this context
def classes
@classes.values
end
##
# All classes and modules in this namespace
def classes_and_modules
classes + modules
end
##
# Hash of classes keyed by class name
def classes_hash
@classes
end
##
# Is part of this thing was defined in +file+?
def defined_in?(file)
@in_files.include?(file)
end
##
# Iterator for attributes
def each_attribute # :yields: attribute
@attributes.each { |a| yield a }
end
##
# Iterator for classes and modules
def each_classmodule(&block) # :yields: module
classes_and_modules.sort.each(&block)
end
##
# Iterator for constants
def each_constant # :yields: constant
@constants.each {|c| yield c}
end
##
# Iterator for included modules
def each_include # :yields: include
@includes.each do |i| yield i end
end
##
# Iterator for methods
def each_method # :yields: method
@method_list.sort.each {|m| yield m}
end
##
# Finds an attribute with +name+ in this context
def find_attribute_named(name)
@attributes.find { |m| m.name == name }
end
##
# Finds a constant with +name+ in this context
def find_constant_named(name)
@constants.find {|m| m.name == name}
end
##
# Find a module at a higher scope
def find_enclosing_module_named(name)
parent && parent.find_module_named(name)
end
##
# Finds a file with +name+ in this context
def find_file_named(name)
top_level.class.find_file_named(name)
end
##
# Finds an instance method with +name+ in this context
def find_instance_method_named(name)
@method_list.find { |meth| meth.name == name && !meth.singleton }
end
##
# Finds a method, constant, attribute, module or files named +symbol+ in
# this context
def find_local_symbol(symbol)
find_method_named(symbol) or
find_constant_named(symbol) or
find_attribute_named(symbol) or
find_module_named(symbol) or
find_file_named(symbol)
end
##
# Finds a instance or module method with +name+ in this context
def find_method_named(name)
@method_list.find { |meth| meth.name == name }
end
##
# Find a module with +name+ using ruby's scoping rules
def find_module_named(name)
res = @modules[name] || @classes[name]
return res if res
return self if self.name == name
find_enclosing_module_named name
end
##
# Look up +symbol+. If +method+ is non-nil, then we assume the symbol
# references a module that contains that method.
def find_symbol(symbol, method = nil)
result = nil
case symbol
when /^::(.*)/ then
result = top_level.find_symbol($1)
when /::/ then
modules = symbol.split(/::/)
unless modules.empty? then
module_name = modules.shift
result = find_module_named(module_name)
if result then
modules.each do |name|
result = result.find_module_named name
break unless result
end
end
end
else
# if a method is specified, then we're definitely looking for
# a module, otherwise it could be any symbol
if method then
result = find_module_named symbol
else
result = find_local_symbol symbol
if result.nil? then
if symbol =~ /^[A-Z]/ then
result = parent
while result && result.name != symbol do
result = result.parent
end
end
end
end
end
if result and method then
fail unless result.respond_to? :find_local_symbol
result = result.find_local_symbol(method)
end
result
end
##
# The full name for this context. This method is overridden by subclasses.
def full_name
'(unknown)'
end
##
# URL for this with a +prefix+
def http_url(prefix)
path = full_name
path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</
path = [prefix] + path.split('::')
File.join(*path.compact) + '.html'
end
##
# Breaks method_list into a nested hash by type (class or instance) and
# visibility (public, protected private)
def methods_by_type
methods = {}
TYPES.each do |type|
visibilities = {}
VISIBILITIES.each do |vis|
visibilities[vis] = []
end
methods[type] = visibilities
end
each_method do |method|
methods[method.type][method.visibility] << method
end
methods
end
##
# Yields Method and Attr entries matching the list of names in +methods+.
# Attributes are only returned when +singleton+ is false.
def methods_matching(methods, singleton = false)
count = 0
@method_list.each do |m|
if methods.include? m.name and m.singleton == singleton then
yield m
count += 1
end
end
return if count == methods.size || singleton
@attributes.each do |a|
yield a if methods.include? a.name
end
end
##
# Array of modules in this context
def modules
@modules.values
end
##
# Hash of modules keyed by module name
def modules_hash
@modules
end
##
# Changes the visibility for new methods to +visibility+
def ongoing_visibility=(visibility)
@visibility = visibility
end
##
# Record which file +top_level+ is in
def record_location(top_level)
@in_files << top_level unless @in_files.include?(top_level)
end
##
# If a class's documentation is turned off after we've started collecting
# methods etc., we need to remove the ones we have
def remove_methods_etc
initialize_methods_etc
end
##
# Given an array +methods+ of method names, set the visibility of each to
# +visibility+
def set_visibility_for(methods, visibility, singleton = false)
methods_matching methods, singleton do |m|
m.visibility = visibility
end
end
##
# Removes classes and modules when we see a :nodoc: all
def remove_classes_and_modules
initialize_classes_and_modules
end
##
# Creates a new section with +title+ and +comment+
def set_current_section(title, comment)
@current_section = Section.new self, title, comment
@sections << @current_section
end
##
# Return the TopLevel that owns us
def top_level
return @top_level if defined? @top_level
@top_level = self
@top_level = @top_level.parent until RDoc::TopLevel === @top_level
@top_level
end
end

View file

@ -1,340 +0,0 @@
# A wonderful hack by to draw package diagrams using the dot package.
# Originally written by Jah, team Enticla.
#
# You must have the V1.7 or later in your path
# http://www.research.att.com/sw/tools/graphviz/
require 'rdoc/dot'
module RDoc
##
# Draw a set of diagrams representing the modules and classes in the
# system. We draw one diagram for each file, and one for each toplevel
# class or module. This means there will be overlap. However, it also
# means that you'll get better context for objects.
#
# To use, simply
#
# d = Diagram.new(info) # pass in collection of top level infos
# d.draw
#
# The results will be written to the +dot+ subdirectory. The process
# also sets the +diagram+ attribute in each object it graphs to
# the name of the file containing the image. This can be used
# by output generators to insert images.
class Diagram
FONT = "Arial"
DOT_PATH = "dot"
##
# Pass in the set of top level objects. The method also creates the
# subdirectory to hold the images
def initialize(info, options)
@info = info
@options = options
@counter = 0
FileUtils.mkdir_p(DOT_PATH)
@diagram_cache = {}
end
##
# Draw the diagrams. We traverse the files, drawing a diagram for each. We
# also traverse each top-level class and module in that file drawing a
# diagram for these too.
def draw
unless @options.quiet
$stderr.print "Diagrams: "
$stderr.flush
end
@info.each_with_index do |i, file_count|
@done_modules = {}
@local_names = find_names(i)
@global_names = []
@global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
'fontname' => FONT,
'fontsize' => '8',
'bgcolor' => 'lightcyan1',
'compound' => 'true')
# it's a little hack %) i'm too lazy to create a separate class
# for default node
graph << DOT::Node.new('name' => 'node',
'fontname' => FONT,
'color' => 'black',
'fontsize' => 8)
i.modules.each do |mod|
draw_module(mod, graph, true, i.file_relative_name)
end
add_classes(i, graph, i.file_relative_name)
i.diagram = convert_to_png("f_#{file_count}", graph)
# now go through and document each top level class and
# module independently
i.modules.each_with_index do |mod, count|
@done_modules = {}
@local_names = find_names(mod)
@global_names = []
@global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
'fontname' => FONT,
'fontsize' => '8',
'bgcolor' => 'lightcyan1',
'compound' => 'true')
graph << DOT::Node.new('name' => 'node',
'fontname' => FONT,
'color' => 'black',
'fontsize' => 8)
draw_module(mod, graph, true)
mod.diagram = convert_to_png("m_#{file_count}_#{count}",
graph)
end
end
$stderr.puts unless @options.quiet
end
private
def find_names(mod)
return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
mod.modules.collect{|m| find_names(m)}.flatten
end
def find_full_name(name, mod)
full_name = name.dup
return full_name if @local_names.include?(full_name)
mod_path = mod.full_name.split('::')[0..-2]
unless mod_path.nil?
until mod_path.empty?
full_name = mod_path.pop + '::' + full_name
return full_name if @local_names.include?(full_name)
end
end
return name
end
def draw_module(mod, graph, toplevel = false, file = nil)
return if @done_modules[mod.full_name] and not toplevel
@counter += 1
url = mod.http_url("classes")
m = DOT::Subgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
'label' => mod.name,
'fontname' => FONT,
'color' => 'blue',
'style' => 'filled',
'URL' => %{"#{url}"},
'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
@done_modules[mod.full_name] = m
add_classes(mod, m, file)
graph << m
unless mod.includes.empty?
mod.includes.each do |inc|
m_full_name = find_full_name(inc.name, mod)
if @local_names.include?(m_full_name)
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
'to' => "#{mod.full_name.gsub( /:/,'_' )}",
'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
else
unless @global_names.include?(m_full_name)
path = m_full_name.split("::")
url = File.join('classes', *path) + ".html"
@global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
'shape' => 'box',
'label' => "#{m_full_name}",
'URL' => %{"#{url}"})
@global_names << m_full_name
end
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
'to' => "#{mod.full_name.gsub( /:/,'_' )}",
'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
end
end
end
end
def add_classes(container, graph, file = nil )
use_fileboxes = @options.fileboxes
files = {}
# create dummy node (needed if empty and for module includes)
if container.full_name
graph << DOT::Node.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
'label' => "",
'width' => (container.classes.empty? and
container.modules.empty?) ?
'0.75' : '0.01',
'height' => '0.01',
'shape' => 'plaintext')
end
container.classes.each_with_index do |cl, cl_index|
last_file = cl.in_files[-1].file_relative_name
if use_fileboxes && !files.include?(last_file)
@counter += 1
files[last_file] =
DOT::Subgraph.new('name' => "cluster_#{@counter}",
'label' => "#{last_file}",
'fontname' => FONT,
'color'=>
last_file == file ? 'red' : 'black')
end
next if cl.name == 'Object' || cl.name[0,2] == "<<"
url = cl.http_url("classes")
label = cl.name.dup
if use_fileboxes && cl.in_files.length > 1
label << '\n[' +
cl.in_files.collect {|i|
i.file_relative_name
}.sort.join( '\n' ) +
']'
end
attrs = {
'name' => "#{cl.full_name.gsub( /:/, '_' )}",
'fontcolor' => 'black',
'style'=>'filled',
'color'=>'palegoldenrod',
'label' => label,
'shape' => 'ellipse',
'URL' => %{"#{url}"}
}
c = DOT::Node.new(attrs)
if use_fileboxes
files[last_file].push c
else
graph << c
end
end
if use_fileboxes
files.each_value do |val|
graph << val
end
end
unless container.classes.empty?
container.classes.each_with_index do |cl, cl_index|
cl.includes.each do |m|
m_full_name = find_full_name(m.name, cl)
if @local_names.include?(m_full_name)
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
'to' => "#{cl.full_name.gsub( /:/,'_' )}",
'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
else
unless @global_names.include?(m_full_name)
path = m_full_name.split("::")
url = File.join('classes', *path) + ".html"
@global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
'shape' => 'box',
'label' => "#{m_full_name}",
'URL' => %{"#{url}"})
@global_names << m_full_name
end
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
'to' => "#{cl.full_name.gsub( /:/, '_')}")
end
end
sclass = cl.superclass
next if sclass.nil? || sclass == 'Object'
sclass_full_name = find_full_name(sclass,cl)
unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
path = sclass_full_name.split("::")
url = File.join('classes', *path) + ".html"
@global_graph << DOT::Node.new('name' => "#{sclass_full_name.gsub( /:/, '_' )}",
'label' => sclass_full_name,
'URL' => %{"#{url}"})
@global_names << sclass_full_name
end
@global_graph << DOT::Edge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
'to' => "#{cl.full_name.gsub( /:/, '_')}")
end
end
container.modules.each do |submod|
draw_module(submod, graph)
end
end
def convert_to_png(file_base, graph)
str = graph.to_s
return @diagram_cache[str] if @diagram_cache[str]
op_type = @options.image_format
dotfile = File.join(DOT_PATH, file_base)
src = dotfile + ".dot"
dot = dotfile + "." + op_type
unless @options.quiet
$stderr.print "."
$stderr.flush
end
File.open(src, 'w+' ) do |f|
f << str << "\n"
end
system "dot", "-T#{op_type}", src, "-o", dot
# Now construct the imagemap wrapper around
# that png
ret = wrap_in_image_map(src, dot)
@diagram_cache[str] = ret
return ret
end
##
# Extract the client-side image map from dot, and use it to generate the
# imagemap proper. Return the whole <map>..<img> combination, suitable for
# inclusion on the page
def wrap_in_image_map(src, dot)
res = ""
dot_map = `dot -Tismap #{src}`
if(!dot_map.empty?)
res << %{<map id="map" name="map">\n}
dot_map.split($/).each do |area|
unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
$stderr.puts "Unexpected output from dot:\n#{area}"
return nil
end
xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
url, area_name = $5, $6
res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
res << %{ href="#{url}" alt="#{area_name}" />\n}
end
res << "</map>\n"
end
res << %{<img src="#{dot}" usemap="#map" alt="#{dot}" />}
return res
end
end
end

View file

@ -1,249 +0,0 @@
module RDoc; end
module RDoc::DOT
TAB = ' '
TAB2 = TAB * 2
# options for node declaration
NODE_OPTS = [
'bgcolor',
'color',
'fontcolor',
'fontname',
'fontsize',
'height',
'width',
'label',
'layer',
'rank',
'shape',
'shapefile',
'style',
'URL',
]
# options for edge declaration
EDGE_OPTS = [
'color',
'decorate',
'dir',
'fontcolor',
'fontname',
'fontsize',
'id',
'label',
'layer',
'lhead',
'ltail',
'minlen',
'style',
'weight'
]
# options for graph declaration
GRAPH_OPTS = [
'bgcolor',
'center',
'clusterrank',
'color',
'compound',
'concentrate',
'fillcolor',
'fontcolor',
'fontname',
'fontsize',
'label',
'layerseq',
'margin',
'mclimit',
'nodesep',
'nslimit',
'ordering',
'orientation',
'page',
'rank',
'rankdir',
'ranksep',
'ratio',
'size',
'style',
'URL'
]
# a root class for any element in dot notation
class SimpleElement
attr_accessor :name
def initialize( params = {} )
@label = params['name'] ? params['name'] : ''
end
def to_s
@name
end
end
# an element that has options ( node, edge or graph )
class Element < SimpleElement
#attr_reader :parent
attr_accessor :name, :options
def initialize( params = {}, option_list = [] )
super( params )
@name = params['name'] ? params['name'] : nil
@parent = params['parent'] ? params['parent'] : nil
@options = {}
option_list.each{ |i|
@options[i] = params[i] if params[i]
}
@options['label'] ||= @name if @name != 'node'
end
def each_option
@options.each{ |i| yield i }
end
def each_option_pair
@options.each_pair{ |key, val| yield key, val }
end
#def parent=( thing )
# @parent.delete( self ) if defined?( @parent ) and @parent
# @parent = thing
#end
end
# this is used when we build nodes that have shape=record
# ports don't have options :)
class Port < SimpleElement
attr_accessor :label
def initialize( params = {} )
super( params )
@name = params['label'] ? params['label'] : ''
end
def to_s
( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
end
end
# node element
class Node < Element
def initialize( params = {}, option_list = NODE_OPTS )
super( params, option_list )
@ports = params['ports'] ? params['ports'] : []
end
def each_port
@ports.each{ |i| yield i }
end
def << ( thing )
@ports << thing
end
def push ( thing )
@ports.push( thing )
end
def pop
@ports.pop
end
def to_s( t = '' )
label = @options['shape'] != 'record' && @ports.length == 0 ?
@options['label'] ?
t + TAB + "label = \"#{@options['label']}\"\n" :
'' :
t + TAB + 'label = "' + " \\\n" +
t + TAB2 + "#{@options['label']}| \\\n" +
@ports.collect{ |i|
t + TAB2 + i.to_s
}.join( "| \\\n" ) + " \\\n" +
t + TAB + '"' + "\n"
t + "#{@name} [\n" +
@options.to_a.collect{ |i|
i[1] && i[0] != 'label' ?
t + TAB + "#{i[0]} = #{i[1]}" : nil
}.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
label +
t + "]\n"
end
end
# subgraph element is the same to graph, but has another header in dot
# notation
class Subgraph < Element
def initialize( params = {}, option_list = GRAPH_OPTS )
super( params, option_list )
@nodes = params['nodes'] ? params['nodes'] : []
@dot_string = 'subgraph'
end
def each_node
@nodes.each{ |i| yield i }
end
def << ( thing )
@nodes << thing
end
def push( thing )
@nodes.push( thing )
end
def pop
@nodes.pop
end
def to_s( t = '' )
hdr = t + "#{@dot_string} #{@name} {\n"
options = @options.to_a.collect{ |name, val|
val && name != 'label' ?
t + TAB + "#{name} = #{val}" :
name ? t + TAB + "#{name} = \"#{val}\"" : nil
}.compact.join( "\n" ) + "\n"
nodes = @nodes.collect{ |i|
i.to_s( t + TAB )
}.join( "\n" ) + "\n"
hdr + options + nodes + t + "}\n"
end
end
# this is graph
class Digraph < Subgraph
def initialize( params = {}, option_list = GRAPH_OPTS )
super( params, option_list )
@dot_string = 'digraph'
end
end
# this is edge
class Edge < Element
attr_accessor :from, :to
def initialize( params = {}, option_list = EDGE_OPTS )
super( params, option_list )
@from = params['from'] ? params['from'] : nil
@to = params['to'] ? params['to'] : nil
end
def to_s( t = '' )
t + "#{@from} -> #{to} [\n" +
@options.to_a.collect{ |i|
i[1] && i[0] != 'label' ?
t + TAB + "#{i[0]} = #{i[1]}" :
i[1] ? t + TAB + "#{i[0]} = \"#{i[1]}\"" : nil
}.compact.join( "\n" ) + "\n" + t + "]\n"
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,113 +0,0 @@
require 'rdoc/generator/html'
class RDoc::Generator::CHM < RDoc::Generator::HTML
HHC_PATH = "c:/Program Files/HTML Help Workshop/hhc.exe"
##
# Standard generator factory
def self.for(options)
new(options)
end
def initialize(*args)
super
@op_name = @options.op_name || "rdoc"
check_for_html_help_workshop
end
def check_for_html_help_workshop
stat = File.stat(HHC_PATH)
rescue
$stderr <<
"\n.chm output generation requires that Microsoft's Html Help\n" <<
"Workshop is installed. RDoc looks for it in:\n\n " <<
HHC_PATH <<
"\n\nYou can download a copy for free from:\n\n" <<
" http://msdn.microsoft.com/library/default.asp?" <<
"url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp\n\n"
end
##
# Generate the html as normal, then wrap it in a help project
def generate(info)
super
@project_name = @op_name + ".hhp"
create_help_project
end
##
# The project contains the project file, a table of contents and an index
def create_help_project
create_project_file
create_contents_and_index
compile_project
end
##
# The project file links together all the various
# files that go to make up the help.
def create_project_file
template = RDoc::TemplatePage.new @template::HPP_FILE
values = { "title" => @options.title, "opname" => @op_name }
files = []
@files.each do |f|
files << { "html_file_name" => f.path }
end
values['all_html_files'] = files
File.open(@project_name, "w") do |f|
template.write_html_on(f, values)
end
end
##
# The contents is a list of all files and modules.
# For each we include as sub-entries the list
# of methods they contain. As we build the contents
# we also build an index file
def create_contents_and_index
contents = []
index = []
(@files+@classes).sort.each do |entry|
content_entry = { "c_name" => entry.name, "ref" => entry.path }
index << { "name" => entry.name, "aref" => entry.path }
internals = []
methods = entry.build_method_summary_list(entry.path)
content_entry["methods"] = methods unless methods.empty?
contents << content_entry
index.concat methods
end
values = { "contents" => contents }
template = RDoc::TemplatePage.new @template::CONTENTS
File.open("contents.hhc", "w") do |f|
template.write_html_on(f, values)
end
values = { "index" => index }
template = RDoc::TemplatePage.new @template::CHM_INDEX
File.open("index.hhk", "w") do |f|
template.write_html_on(f, values)
end
end
##
# Invoke the windows help compiler to compiler the project
def compile_project
system(HHC_PATH, @project_name)
end
end

View file

@ -1,100 +0,0 @@
require 'rdoc/generator/chm'
require 'rdoc/generator/html/html'
module RDoc::Generator::CHM::CHM
HTML = RDoc::Generator::HTML::HTML
INDEX = HTML::INDEX
STYLE = HTML::STYLE
CLASS_INDEX = HTML::CLASS_INDEX
CLASS_PAGE = HTML::CLASS_PAGE
FILE_INDEX = HTML::FILE_INDEX
FILE_PAGE = HTML::FILE_PAGE
METHOD_INDEX = HTML::METHOD_INDEX
METHOD_LIST = HTML::METHOD_LIST
FR_INDEX_BODY = HTML::FR_INDEX_BODY
# This is a nasty little hack, but hhc doesn't support the <?xml tag, so...
BODY = HTML::BODY.sub!(/<\?xml.*\?>/, '')
SRC_PAGE = HTML::SRC_PAGE.sub!(/<\?xml.*\?>/, '')
HPP_FILE = <<-EOF
[OPTIONS]
Auto Index = Yes
Compatibility=1.1 or later
Compiled file=<%= values["opname"] %>.chm
Contents file=contents.hhc
Full-text search=Yes
Index file=index.hhk
Language=0x409 English(United States)
Title=<%= values["title"] %>
[FILES]
<% values["all_html_files"].each do |all_html_files| %>
<%= all_html_files["html_file_name"] %>
<% end # values["all_html_files"] %>
EOF
CONTENTS = <<-EOF
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
<!-- Sitemap 1.0 -->
</HEAD><BODY>
<OBJECT type="text/site properties">
<param name="Foreground" value="0x80">
<param name="Window Styles" value="0x800025">
<param name="ImageType" value="Folder">
</OBJECT>
<UL>
<% values["contents"].each do |contents| %>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="<%= contents["c_name"] %>">
<param name="Local" value="<%= contents["ref"] %>">
</OBJECT>
<% if contents["methods"] then %>
<ul>
<% contents["methods"].each do |methods| %>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="<%= methods["name"] %>">
<param name="Local" value="<%= methods["aref"] %>">
</OBJECT>
<% end # contents["methods"] %>
</ul>
<% end %>
</LI>
<% end # values["contents"] %>
</UL>
</BODY></HTML>
EOF
CHM_INDEX = <<-EOF
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
<!-- Sitemap 1.0 -->
</HEAD><BODY>
<OBJECT type="text/site properties">
<param name="Foreground" value="0x80">
<param name="Window Styles" value="0x800025">
<param name="ImageType" value="Folder">
</OBJECT>
<UL>
<% values["index"].each do |index| %>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="<%= index["name"] %>">
<param name="Local" value="<%= index["aref"] %>">
</OBJECT>
<% end # values["index"] %>
</UL>
</BODY></HTML>
EOF
end

View file

@ -0,0 +1,459 @@
#!ruby
# vim: noet ts=2 sts=8 sw=2
unless File.exist? File.expand_path('../.svn', __FILE__) then
require 'rubygems'
gem 'rdoc', '>= 2.4'
end
require 'pp'
require 'pathname'
require 'fileutils'
require 'erb'
require 'rdoc/generator/markup'
$DARKFISH_DRYRUN = false # TODO make me non-global
#
# Darkfish RDoc HTML Generator
#
# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
#
# == Author/s
# * Michael Granger (ged@FaerieMUD.org)
#
# == Contributors
# * Mahlon E. Smith (mahlon@martini.nu)
# * Eric Hodel (drbrain@segment7.net)
#
# == License
#
# Copyright (c) 2007, 2008, Michael Granger. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the author/s, nor the names of the project's
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
class RDoc::Generator::Darkfish
RDoc::RDoc.add_generator( self )
include ERB::Util
# Subversion rev
SVNRev = %$Rev: 52 $
# Subversion ID
SVNId = %$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
# Path to this file's parent directory. Used to find templates and other
# resources.
GENERATOR_DIR = File.join 'rdoc', 'generator'
# Release Version
VERSION = '1.1.6'
# Directory where generated classes live relative to the root
CLASS_DIR = nil
# Directory where generated files live relative to the root
FILE_DIR = nil
#################################################################
### C L A S S M E T H O D S
#################################################################
### Standard generator factory method
def self::for( options )
new( options )
end
#################################################################
### I N S T A N C E M E T H O D S
#################################################################
### Initialize a few instance variables before we start
def initialize( options )
@options = options
template = @options.template || 'darkfish'
template_dir = $LOAD_PATH.map do |path|
File.join File.expand_path(path), GENERATOR_DIR, 'template', template
end.find do |dir|
File.directory? dir
end
raise RDoc::Error, "could not find template #{template.inspect}" unless
template_dir
@template_dir = Pathname.new File.expand_path(template_dir)
@files = nil
@classes = nil
@basedir = Pathname.pwd.expand_path
end
######
public
######
# The output directory
attr_reader :outputdir
### Output progress information if debugging is enabled
def debug_msg( *msg )
return unless $DEBUG_RDOC
$stderr.puts( *msg )
end
def class_dir
CLASS_DIR
end
def file_dir
FILE_DIR
end
### Create the directories the generated docs will live in if
### they don't already exist.
def gen_sub_directories
@outputdir.mkpath
end
### Copy over the stylesheet into the appropriate place in the output
### directory.
def write_style_sheet
debug_msg "Copying static files"
options = { :verbose => $DEBUG_RDOC, :noop => $DARKFISH_DRYRUN }
FileUtils.cp @template_dir + 'rdoc.css', '.', options
Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
next if File.directory? path
next if path =~ /#{File::SEPARATOR}\./
dst = Pathname.new(path).relative_path_from @template_dir
# I suck at glob
dst_dir = dst.dirname
FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
FileUtils.cp @template_dir + path, dst, options
end
end
### Build the initial indices and output objects
### based on an array of TopLevel objects containing
### the extracted information.
def generate( top_levels )
@outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
@files = top_levels.sort
@classes = RDoc::TopLevel.all_classes_and_modules.sort
@methods = @classes.map { |m| m.method_list }.flatten.sort
@modsort = get_sorted_module_list( @classes )
# Now actually write the output
write_style_sheet
generate_index
generate_class_files
generate_file_files
rescue StandardError => err
debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
raise
end
#########
protected
#########
### Return a list of the documented modules sorted by salience first, then
### by name.
def get_sorted_module_list( classes )
nscounts = classes.inject({}) do |counthash, klass|
top_level = klass.full_name.gsub( /::.*/, '' )
counthash[top_level] ||= 0
counthash[top_level] += 1
counthash
end
# Sort based on how often the top level namespace occurs, and then on the
# name of the module -- this works for projects that put their stuff into
# a namespace, of course, but doesn't hurt if they don't.
classes.sort_by do |klass|
top_level = klass.full_name.gsub( /::.*/, '' )
[
nscounts[ top_level ] * -1,
klass.full_name
]
end.select do |klass|
klass.document_self
end
end
### Generate an index page which lists all the classes which
### are documented.
def generate_index
template_file = @template_dir + 'index.rhtml'
return unless template_file.exist?
debug_msg "Rendering the index page..."
template_src = template_file.read
template = ERB.new( template_src, nil, '<>' )
template.filename = template_file.to_s
context = binding()
output = nil
begin
output = template.result( context )
rescue NoMethodError => err
raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
template_file,
err.message,
eval( "_erbout[-50,50]", context )
], err.backtrace
end
outfile = @basedir + @options.op_dir + 'index.html'
unless $DARKFISH_DRYRUN
debug_msg "Outputting to %s" % [outfile.expand_path]
outfile.open( 'w', 0644 ) do |fh|
fh.print( output )
end
else
debug_msg "Would have output to %s" % [outfile.expand_path]
end
end
### Generate a documentation file for each class
def generate_class_files
template_file = @template_dir + 'classpage.rhtml'
return unless template_file.exist?
debug_msg "Generating class documentation in #@outputdir"
@classes.each do |klass|
debug_msg " working on %s (%s)" % [ klass.full_name, klass.path ]
outfile = @outputdir + klass.path
rel_prefix = @outputdir.relative_path_from( outfile.dirname )
svninfo = self.get_svninfo( klass )
debug_msg " rendering #{outfile}"
self.render_template( template_file, binding(), outfile )
end
end
### Generate a documentation file for each file
def generate_file_files
template_file = @template_dir + 'filepage.rhtml'
return unless template_file.exist?
debug_msg "Generating file documentation in #@outputdir"
@files.each do |file|
outfile = @outputdir + file.path
debug_msg " working on %s (%s)" % [ file.full_name, outfile ]
rel_prefix = @outputdir.relative_path_from( outfile.dirname )
context = binding()
debug_msg " rendering #{outfile}"
self.render_template( template_file, binding(), outfile )
end
end
### Return a string describing the amount of time in the given number of
### seconds in terms a human can understand easily.
def time_delta_string( seconds )
return 'less than a minute' if seconds < 1.minute
return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
return 'about one hour' if seconds < 90.minutes
return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
return 'one day' if seconds < 1.day
return 'about one day' if seconds < 2.days
return (seconds / 1.day).to_s + ' days' if seconds < 1.week
return 'about one week' if seconds < 2.week
return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
return (seconds / 1.month).to_s + ' months' if seconds < 1.year
return (seconds / 1.year).to_s + ' years'
end
# %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
SVNID_PATTERN = /
\$Id:\s
(\S+)\s # filename
(\d+)\s # rev
(\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
(\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
(\w+)\s # committer
\$$
/x
### Try to extract Subversion information out of the first constant whose value looks like
### a subversion Id tag. If no matching constant is found, and empty hash is returned.
def get_svninfo( klass )
constants = klass.constants or return {}
constants.find {|c| c.value =~ SVNID_PATTERN } or return {}
filename, rev, date, time, committer = $~.captures
commitdate = Time.parse( date + ' ' + time )
return {
:filename => filename,
:rev => Integer( rev ),
:commitdate => commitdate,
:commitdelta => time_delta_string( Time.now.to_i - commitdate.to_i ),
:committer => committer,
}
end
### Load and render the erb template in the given +template_file+ within the
### specified +context+ (a Binding object) and write it out to +outfile+.
### Both +template_file+ and +outfile+ should be Pathname-like objects.
def render_template( template_file, context, outfile )
template_src = template_file.read
template = ERB.new( template_src, nil, '<>' )
template.filename = template_file.to_s
output = begin
template.result( context )
rescue NoMethodError => err
raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
template_file.to_s,
err.message,
eval( "_erbout[-50,50]", context )
], err.backtrace
end
unless $DARKFISH_DRYRUN
outfile.dirname.mkpath
outfile.open( 'w', 0644 ) do |ofh|
ofh.print( output )
end
else
debug_msg " would have written %d bytes to %s" %
[ output.length, outfile ]
end
end
end # Roc::Generator::Darkfish
# :stopdoc:
### Time constants
module TimeConstantMethods # :nodoc:
### Number of seconds (returns receiver unmodified)
def seconds
return self
end
alias_method :second, :seconds
### Returns number of seconds in <receiver> minutes
def minutes
return self * 60
end
alias_method :minute, :minutes
### Returns the number of seconds in <receiver> hours
def hours
return self * 60.minutes
end
alias_method :hour, :hours
### Returns the number of seconds in <receiver> days
def days
return self * 24.hours
end
alias_method :day, :days
### Return the number of seconds in <receiver> weeks
def weeks
return self * 7.days
end
alias_method :week, :weeks
### Returns the number of seconds in <receiver> fortnights
def fortnights
return self * 2.weeks
end
alias_method :fortnight, :fortnights
### Returns the number of seconds in <receiver> months (approximate)
def months
return self * 30.days
end
alias_method :month, :months
### Returns the number of seconds in <receiver> years (approximate)
def years
return (self * 365.25.days).to_i
end
alias_method :year, :years
### Returns the Time <receiver> number of seconds before the
### specified +time+. E.g., 2.hours.before( header.expiration )
def before( time )
return time - self
end
### Returns the Time <receiver> number of seconds ago. (e.g.,
### expiration > 2.hours.ago )
def ago
return self.before( ::Time.now )
end
### Returns the Time <receiver> number of seconds after the given +time+.
### E.g., 10.minutes.after( header.expiration )
def after( time )
return time + self
end
# Reads best without arguments: 10.minutes.from_now
def from_now
return self.after( ::Time.now )
end
end # module TimeConstantMethods
# Extend Numeric with time constants
class Numeric # :nodoc:
include TimeConstantMethods
end

View file

@ -1,449 +0,0 @@
require 'fileutils'
require 'rdoc/generator'
require 'rdoc/markup/to_html'
##
# We're responsible for generating all the HTML files from the object tree
# defined in code_objects.rb. We generate:
#
# [files] an html file for each input file given. These
# input files appear as objects of class
# TopLevel
#
# [classes] an html file for each class or module encountered.
# These classes are not grouped by file: if a file
# contains four classes, we'll generate an html
# file for the file itself, and four html files
# for the individual classes.
#
# [indices] we generate three indices for files, classes,
# and methods. These are displayed in a browser
# like window with three index panes across the
# top and the selected description below
#
# Method descriptions appear in whatever entity (file, class, or module) that
# contains them.
#
# We generate files in a structure below a specified subdirectory, normally
# +doc+.
#
# opdir
# |
# |___ files
# | |__ per file summaries
# |
# |___ classes
# |__ per class/module descriptions
#
# HTML is generated using the Template class.
class RDoc::Generator::HTML
include RDoc::Generator::MarkUp
##
# Generator may need to return specific subclasses depending on the
# options they are passed. Because of this we create them using a factory
def self.for(options)
RDoc::Generator::AllReferences.reset
RDoc::Generator::Method.reset
if options.all_one_file
RDoc::Generator::HTMLInOne.new options
else
new options
end
end
class << self
protected :new
end
##
# Set up a new HTML generator. Basically all we do here is load up the
# correct output temlate
def initialize(options) #:not-new:
@options = options
load_html_template
end
##
# Build the initial indices and output objects
# based on an array of TopLevel objects containing
# the extracted information.
def generate(toplevels)
@toplevels = toplevels
@files = []
@classes = []
write_style_sheet
gen_sub_directories
build_indices
generate_html
end
private
##
# Load up the HTML template specified in the options.
# If the template name contains a slash, use it literally
def load_html_template
#
# If the template is not a path, first look for it
# in rdoc's HTML template directory. Perhaps this behavior should
# be reversed (first try to include the template and, only if that
# fails, try to include it in the default template directory).
# One danger with reversing the behavior, however, is that
# if something like require 'html' could load up an
# unrelated file in the standard library or in a gem.
#
template = @options.template
unless template =~ %r{/|\\} then
template = File.join('rdoc', 'generator', @options.generator.key,
template)
end
begin
require template
@template = self.class.const_get @options.template.upcase
@options.template_class = @template
rescue LoadError => e
#
# The template did not exist in the default template directory, so
# see if require can find the template elsewhere (in a gem, for
# instance).
#
if(e.message[template] && template != @options.template)
template = @options.template
retry
end
$stderr.puts "Could not find HTML template '#{template}': #{e.message}"
exit 99
end
end
##
# Write out the style sheet used by the main frames
def write_style_sheet
return unless @template.constants.include? :STYLE or
@template.constants.include? 'STYLE'
template = RDoc::TemplatePage.new @template::STYLE
unless @options.css then
open RDoc::Generator::CSS_NAME, 'w' do |f|
values = {}
if @template.constants.include? :FONTS or
@template.constants.include? 'FONTS' then
values["fonts"] = @template::FONTS
end
template.write_html_on(f, values)
end
end
end
##
# See the comments at the top for a description of the directory structure
def gen_sub_directories
FileUtils.mkdir_p RDoc::Generator::FILE_DIR
FileUtils.mkdir_p RDoc::Generator::CLASS_DIR
rescue
$stderr.puts $!.message
exit 1
end
def build_indices
@files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
@options)
end
##
# Generate all the HTML
def generate_html
@main_url = main_url
# the individual descriptions for files and classes
gen_into(@files)
gen_into(@classes)
# and the index files
gen_file_index
gen_class_index
gen_method_index
gen_main_index
# this method is defined in the template file
values = {
'title_suffix' => CGI.escapeHTML("[#{@options.title}]"),
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
@template.write_extra_pages(values) if @template.respond_to?(:write_extra_pages)
end
def gen_into(list)
#
# The file, class, and method lists technically should be regenerated
# for every output file, in order that the relative links be correct
# (we are worried here about frameless templates, which need this
# information for every generated page). Doing this is a bit slow,
# however. For a medium-sized gem, this increased rdoc's runtime by
# about 5% (using the 'time' command-line utility). While this is not
# necessarily a problem, I do not want to pessimize rdoc for large
# projects, however, and so we only regenerate the lists when the
# directory of the output file changes, which seems like a reasonable
# optimization.
#
file_list = {}
class_list = {}
method_list = {}
prev_op_dir = nil
list.each do |item|
next unless item.document_self
op_file = item.path
op_dir = File.dirname(op_file)
if(op_dir != prev_op_dir)
file_list = index_to_links op_file, @files
class_list = index_to_links op_file, @classes
method_list = index_to_links op_file, RDoc::Generator::Method.all_methods
end
prev_op_dir = op_dir
FileUtils.mkdir_p op_dir
open op_file, 'w' do |io|
item.write_on io, file_list, class_list, method_list
end
file_list.clear
class_list.clear
method_list.clear
end
end
def gen_file_index
gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html"
end
def gen_class_index
gen_an_index(@classes, 'Classes', @template::CLASS_INDEX,
"fr_class_index.html")
end
def gen_method_index
gen_an_index(RDoc::Generator::Method.all_methods, 'Methods',
@template::METHOD_INDEX, "fr_method_index.html")
end
def gen_an_index(collection, title, template, filename)
template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template
res = []
collection.sort.each do |f|
if f.document_self
res << { "href" => f.path, "name" => f.index_name }
end
end
values = {
"entries" => res,
'title' => CGI.escapeHTML("#{title} [#{@options.title}]"),
'list_title' => CGI.escapeHTML(title),
'index_url' => @main_url,
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
open filename, 'w' do |f|
template.write_html_on(f, values)
end
end
##
# The main index page is mostly a template frameset, but includes the
# initial page. If the <tt>--main</tt> option was given, we use this as
# our main page, otherwise we use the first file specified on the command
# line.
def gen_main_index
if @template.const_defined? :FRAMELESS then
#
# If we're using a template without frames, then just redirect
# to it from index.html.
#
# One alternative to this, expanding the main page's template into
# index.html, is tricky because the relative URLs will be different
# (since index.html is located in at the site's root,
# rather than within a files or a classes subdirectory).
#
open 'index.html', 'w' do |f|
f.puts(%{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">})
f.puts(%{<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">})
f.puts(%{<head>})
f.puts(%{<title>#{CGI.escapeHTML(@options.title)}</title>})
f.puts(%{<meta http-equiv="refresh" content="0; url=#{@main_url}" />})
f.puts(%{</head>})
f.puts(%{<body></body>})
f.puts(%{</html>})
end
else
main = RDoc::TemplatePage.new @template::INDEX
open 'index.html', 'w' do |f|
style_url = style_url '', @options.css
classes = @classes.sort.map { |klass| klass.value_hash }
values = {
'initial_page' => @main_url,
'style_url' => style_url('', @options.css),
'title' => CGI.escapeHTML(@options.title),
'charset' => @options.charset,
'classes' => classes,
}
values['inline_source'] = @options.inline_source
main.write_html_on f, values
end
end
end
def index_to_links(output_path, collection)
collection.sort.map do |f|
next unless f.document_self
{ "href" => RDoc::Markup::ToHtml.gen_relative_url(output_path, f.path),
"name" => f.index_name }
end.compact
end
##
# Returns the url of the main page
def main_url
main_page = @options.main_page
#
# If a main page has been specified (--main), then search for it
# in the AllReferences array. This allows either files or classes
# to be used for the main page.
#
if main_page then
main_page_ref = RDoc::Generator::AllReferences[main_page]
if main_page_ref then
return main_page_ref.path
else
$stderr.puts "Could not find main page #{main_page}"
end
end
#
# No main page has been specified, so just use the README.
#
@files.each do |file|
if file.name =~ /^README/ then
return file.path
end
end
#
# There's no README (shame! shame!). Just use the first file
# that will be documented.
#
@files.each do |file|
if file.document_self then
return file.path
end
end
#
# There are no files to be documented... Something seems very wrong.
#
raise RDoc::Error, "Couldn't find anything to document (perhaps :stopdoc: has been used in all classes)!"
end
private :main_url
end
class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
def initialize(*args)
super
end
##
# Build the initial indices and output objects
# based on an array of TopLevel objects containing
# the extracted information.
def generate(info)
@toplevels = info
@hyperlinks = {}
build_indices
generate_xml
end
##
# Generate:
#
# * a list of RDoc::Generator::File objects for each TopLevel object.
# * a list of RDoc::Generator::Class objects for each first level
# class or module in the TopLevel objects
# * a complete list of all hyperlinkable terms (file,
# class, module, and method names)
def build_indices
@files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
@options)
end
##
# Generate all the HTML. For the one-file case, we generate
# all the information in to one big hash
def generate_xml
values = {
'charset' => @options.charset,
'files' => gen_into(@files),
'classes' => gen_into(@classes),
'title' => CGI.escapeHTML(@options.title),
}
template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name
opfile = open @options.op_name, 'w'
else
opfile = $stdout
end
template.write_html_on(opfile, values)
end
def gen_into(list)
res = []
list.each do |item|
res << item.value_hash
end
res
end
end

View file

@ -1,24 +0,0 @@
#
# The templates require further refactoring. In particular,
# * Some kind of HTML generation library should be used.
#
# Also, all of the templates require some TLC from a designer.
#
# Right now, this file contains some constants that are used by all
# of the templates.
#
module RDoc::Generator::HTML::Common
XHTML_STRICT_PREAMBLE = <<-EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
EOF
XHTML_FRAME_PREAMBLE = <<-EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
EOF
HTML_ELEMENT = <<-EOF
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
EOF
end

View file

@ -1,92 +0,0 @@
require 'rdoc/generator/html/html'
##
# = CSS2 RDoc HTML template
#
# This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
# bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to
# toggle the visbility of each method's source with each click on the '[source]'
# link.
#
# Frameless basically is the html template without frames.
#
# == Authors
#
# * Michael Granger <ged@FaerieMUD.org>
#
# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
#
# This work is licensed under the Creative Commons Attribution License. To view
# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
# 94305, USA.
module RDoc::Generator::HTML::FRAMELESS
FRAMELESS = true
FONTS = RDoc::Generator::HTML::HTML::FONTS
STYLE = RDoc::Generator::HTML::HTML::STYLE
HEADER = RDoc::Generator::HTML::HTML::HEADER
FOOTER = <<-EOF
<div id="popupmenu" class="index">
<br />
<h1 class="index-entries section-bar">Files</h1>
<ul>
<% values["file_list"].each do |file| %>
<li><a href="<%= file["href"] %>"><%= file["name"] %></a></li>
<% end %>
</ul>
<br />
<h1 class="index-entries section-bar">Classes</h1>
<ul>
<% values["class_list"].each do |klass| %>
<li><a href="<%= klass["href"] %>"><%= klass["name"] %></a></li>
<% end %>
</ul>
<br />
<h1 class="index-entries section-bar">Methods</h1>
<ul>
<% values["method_list"].each do |method| %>
<li><a href="<%= method["href"] %>"><%= method["name"] %></a></li>
<% end %>
</ul>
</div>
</body>
</html>
EOF
FILE_PAGE = RDoc::Generator::HTML::HTML::FILE_PAGE
CLASS_PAGE = RDoc::Generator::HTML::HTML::CLASS_PAGE
METHOD_LIST = RDoc::Generator::HTML::HTML::METHOD_LIST
BODY = HEADER + %{
<%= template_include %> <!-- banner header -->
<div id="bodyContent">
} + METHOD_LIST + %{
</div>
} + FOOTER
SRC_PAGE = RDoc::Generator::HTML::HTML::SRC_PAGE
FR_INDEX_BODY = RDoc::Generator::HTML::HTML::FR_INDEX_BODY
FILE_INDEX = RDoc::Generator::HTML::HTML::FILE_INDEX
CLASS_INDEX = RDoc::Generator::HTML::HTML::CLASS_INDEX
METHOD_INDEX = RDoc::Generator::HTML::HTML::METHOD_INDEX
end

View file

@ -1,150 +0,0 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::HEFSS
FONTS = "Verdana, Arial, Helvetica, sans-serif"
CENTRAL_STYLE = <<-EOF
body,p { font-family: <%= values["fonts"] %>;
color: #000040; background: #BBBBBB;
}
td { font-family: <%= values["fonts"] %>;
color: #000040;
}
.attr-rw { font-size: small; color: #444488 }
.title-row {color: #eeeeff;
background: #BBBBDD;
}
.big-title-font { color: white;
font-family: <%= values["fonts"] %>;
font-size: large;
height: 50px}
.small-title-font { color: purple;
font-family: <%= values["fonts"] %>;
font-size: small; }
.aqua { color: purple }
#diagram img {
border: 0;
}
.method-name, attr-name {
font-family: monospace; font-weight: bold;
}
.tablesubtitle {
width: 100%;
margin-top: 1ex;
margin-bottom: .5ex;
padding: 5px 0px 5px 20px;
font-size: large;
color: purple;
background: #BBBBCC;
}
.tablesubsubtitle {
width: 100%;
margin-top: 1ex;
margin-bottom: .5ex;
padding: 5px 0px 5px 20px;
font-size: medium;
color: white;
background: #BBBBCC;
}
.name-list {
font-family: monospace;
margin-left: 40px;
margin-bottom: 2ex;
line-height: 140%;
}
.description {
margin-left: 40px;
margin-bottom: 2ex;
line-height: 140%;
}
.methodtitle {
font-size: medium;
text_decoration: none;
padding: 3px 3px 3px 20px;
color: #0000AA;
}
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; }
.ruby-ivar { color: #2233dd; }
.ruby-keyword { color: #3333FF; font-weight: bold }
.ruby-node { color: #777777; }
.ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic }
.srcbut { float: right }
EOF
INDEX_STYLE = <<-EOF
body {
background-color: #bbbbbb;
font-family: #{FONTS};
font-size: 11px;
font-style: normal;
line-height: 14px;
color: #000040;
}
div.banner {
background: #bbbbcc;
color: white;
padding: 1;
margin: 0;
font-size: 90%;
font-weight: bold;
line-height: 1.1;
text-align: center;
width: 100%;
}
EOF
FACTORY = RDoc::Generator::HTML::
KilmerFactory.new(:central_css => CENTRAL_STYLE,
:index_css => INDEX_STYLE,
:method_list_heading => "Subroutines and Functions",
:class_and_module_list_heading => "Classes and Modules",
:attribute_list_heading => "Arguments")
STYLE = FACTORY.get_STYLE()
METHOD_LIST = FACTORY.get_METHOD_LIST()
BODY = FACTORY.get_BODY()
FILE_PAGE = FACTORY.get_FILE_PAGE()
CLASS_PAGE = FACTORY.get_CLASS_PAGE()
SRC_PAGE = FACTORY.get_SRC_PAGE()
FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
FILE_INDEX = FACTORY.get_FILE_INDEX()
CLASS_INDEX = FACTORY.get_CLASS_INDEX()
METHOD_INDEX = FACTORY.get_METHOD_INDEX()
INDEX = FACTORY.get_INDEX()
def self.write_extra_pages(values)
FACTORY.write_extra_pages(values)
end
end

View file

@ -1,769 +0,0 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/common'
##
# = CSS2 RDoc HTML template
#
# This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
# bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to
# toggle the visibility of each method's source with each click on the
# '[source]' link.
#
# This template *also* forms the basis of the frameless template.
#
# == Authors
#
# * Michael Granger <ged@FaerieMUD.org>
#
# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
#
# This work is licensed under the Creative Commons Attribution License. To
# view a copy of this license, visit
# http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative
# Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
module RDoc::Generator::HTML::HTML
include RDoc::Generator::HTML::Common
FONTS = "Verdana,Arial,Helvetica,sans-serif"
STYLE = <<-EOF
body {
font-family: #{FONTS};
font-size: 90%;
margin: 0;
margin-left: 40px;
padding: 0;
background: white;
color: black;
}
h1, h2, h3, h4 {
margin: 0;
background: transparent;
}
h1 {
font-size: 150%;
}
h2,h3,h4 {
margin-top: 1em;
}
:link, :visited {
background: #eef;
color: #039;
text-decoration: none;
}
:link:hover, :visited:hover {
background: #039;
color: #eef;
}
/* Override the base stylesheet's Anchor inside a table cell */
td > :link, td > :visited {
background: transparent;
color: #039;
text-decoration: none;
}
/* and inside a section title */
.section-title > :link, .section-title > :visited {
background: transparent;
color: #eee;
text-decoration: none;
}
/* === Structural elements =================================== */
.index {
margin: 0;
margin-left: -40px;
padding: 0;
font-size: 90%;
}
.index :link, .index :visited {
margin-left: 0.7em;
}
.index .section-bar {
margin-left: 0px;
padding-left: 0.7em;
background: #ccc;
font-size: small;
}
#classHeader, #fileHeader {
width: auto;
color: white;
padding: 0.5em 1.5em 0.5em 1.5em;
margin: 0;
margin-left: -40px;
border-bottom: 3px solid #006;
}
#classHeader :link, #fileHeader :link,
#classHeader :visited, #fileHeader :visited {
background: inherit;
color: white;
}
#classHeader td, #fileHeader td {
background: inherit;
color: white;
}
#fileHeader {
background: #057;
}
#classHeader {
background: #048;
}
.class-name-in-header {
font-size: 180%;
font-weight: bold;
}
#bodyContent {
padding: 0 1.5em 0 1.5em;
}
#description {
padding: 0.5em 1.5em;
background: #efefef;
border: 1px dotted #999;
}
#description h1, #description h2, #description h3,
#description h4, #description h5, #description h6 {
color: #125;
background: transparent;
}
#validator-badges {
text-align: center;
}
#validator-badges img {
border: 0;
}
#copyright {
color: #333;
background: #efefef;
font: 0.75em sans-serif;
margin-top: 5em;
margin-bottom: 0;
padding: 0.5em 2em;
}
/* === Classes =================================== */
table.header-table {
color: white;
font-size: small;
}
.type-note {
font-size: small;
color: #dedede;
}
.section-bar {
color: #333;
border-bottom: 1px solid #999;
margin-left: -20px;
}
.section-title {
background: #79a;
color: #eee;
padding: 3px;
margin-top: 2em;
margin-left: -30px;
border: 1px solid #999;
}
.top-aligned-row {
vertical-align: top
}
.bottom-aligned-row {
vertical-align: bottom
}
#diagram img {
border: 0;
}
/* --- Context section classes ----------------------- */
.context-row { }
.context-item-name {
font-family: monospace;
font-weight: bold;
color: black;
}
.context-item-value {
font-size: small;
color: #448;
}
.context-item-desc {
color: #333;
padding-left: 2em;
}
/* --- Method classes -------------------------- */
.method-detail {
background: #efefef;
padding: 0;
margin-top: 0.5em;
margin-bottom: 1em;
border: 1px dotted #ccc;
}
.method-heading {
color: black;
background: #ccc;
border-bottom: 1px solid #666;
padding: 0.2em 0.5em 0 0.5em;
}
.method-signature {
color: black;
background: inherit;
}
.method-name {
font-weight: bold;
}
.method-args {
font-style: italic;
}
.method-description {
padding: 0 0.5em 0 0.5em;
}
/* --- Source code sections -------------------- */
:link.source-toggle, :visited.source-toggle {
font-size: 90%;
}
div.method-source-code {
background: #262626;
color: #ffdead;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: auto;
}
div.method-source-code pre {
color: #ffdead;
}
/* --- Ruby keyword styles --------------------- */
.standalone-code {
background: #221111;
color: #ffdead;
overflow: auto;
}
.ruby-constant {
color: #7fffd4;
background: transparent;
}
.ruby-keyword {
color: #00ffff;
background: transparent;
}
.ruby-ivar {
color: #eedd82;
background: transparent;
}
.ruby-operator {
color: #00ffee;
background: transparent;
}
.ruby-identifier {
color: #ffdead;
background: transparent;
}
.ruby-node {
color: #ffa07a;
background: transparent;
}
.ruby-comment {
color: #b22222;
font-weight: bold;
background: transparent;
}
.ruby-regexp {
color: #ffa07a;
background: transparent;
}
.ruby-value {
color: #7fffd4;
background: transparent;
}
EOF
#####################################################################
### H E A D E R T E M P L A T E
#####################################################################
HEADER = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript">
// <![CDATA[
function popupCode( url ) {
window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
}
function toggleCode( id ) {
if ( document.getElementById )
elem = document.getElementById( id );
else if ( document.all )
elem = eval( "document.all." + id );
else
return false;
elemStyle = elem.style;
if ( elemStyle.display != "block" ) {
elemStyle.display = "block"
} else {
elemStyle.display = "none"
}
return true;
}
// Make codeblocks hidden by default
document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }<\\/style>" )
// ]]>
</script>
</head>
<body>
EOF
#####################################################################
### F O O T E R T E M P L A T E
#####################################################################
FOOTER = <<-EOF
<div id="validator-badges">
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
</div>
</body>
</html>
EOF
#####################################################################
### F I L E P A G E H E A D E R T E M P L A T E
#####################################################################
FILE_PAGE = <<-EOF
<div id="fileHeader">
<h1><%= values["short_name"] %></h1>
<table class="header-table">
<tr class="top-aligned-row">
<td><strong>Path:</strong></td>
<td><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr class="top-aligned-row">
<td><strong>Last Update:</strong></td>
<td><%= values["dtm_modified"] %></td>
</tr>
</table>
</div>
EOF
#####################################################################
### C L A S S P A G E H E A D E R T E M P L A T E
#####################################################################
CLASS_PAGE = <<-EOF
<div id="classHeader">
<table class="header-table">
<tr class="top-aligned-row">
<td><strong><%= values["classmod"] %></strong></td>
<td class="class-name-in-header"><%= values["full_name"] %></td>
</tr>
<tr class="top-aligned-row">
<td><strong>In:</strong></td>
<td>
<% values["infiles"].each do |infiles| %>
<% if infiles["full_path_url"] then %>
<a href="<%= infiles["full_path_url"] %>">
<% end %>
<%= infiles["full_path"] %>
<% if infiles["full_path_url"] then %>
</a>
<% end %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<br />
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr class="top-aligned-row">
<td><strong>Parent:</strong></td>
<td>
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</div>
EOF
#####################################################################
### M E T H O D L I S T T E M P L A T E
#####################################################################
METHOD_LIST = <<-EOF
<div id="contextContent">
<% if values["diagram"] then %>
<div id="diagram">
<%= values["diagram"] %>
</div>
<% end
if values["description"] then %>
<div id="description">
<%= values["description"] %>
</div>
<% end
if values["requires"] then %>
<div id="requires-list">
<h3 class="section-bar">Required files</h3>
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>&nbsp;&nbsp;
<% end %><%# values["requires"] %>
</div>
</div>
<% end
if values["toc"] then %>
<div id="contents-list">
<h3 class="section-bar">Contents</h3>
<ul>
<% values["toc"].each do |toc| %>
<li><a href="#<%= toc["href"] %>"><%= toc["secname"] %></a></li>
<% end %><%# values["toc"] %>
</ul>
<% end %>
</div>
<% if values["methods"] then %>
<div id="method-list">
<h3 class="section-bar">Methods</h3>
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>&nbsp;&nbsp;
<% end %><%# values["methods"] %>
</div>
</div>
<% end %>
</div>
<!-- if includes -->
<% if values["includes"] then %>
<div id="includes">
<h3 class="section-bar">Included Modules</h3>
<div id="includes-list">
<% values["includes"].each do |includes| %>
<span class="include-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
</div>
<% end
values["sections"].each do |sections| %>
<div id="section">
<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
<% end
end
if sections["classlist"] then %>
<div id="class-list">
<h3 class="section-bar">Classes and Modules</h3>
<%= sections["classlist"] %>
</div>
<% end
if sections["constants"] then %>
<div id="constants-list">
<h3 class="section-bar">Constants</h3>
<div class="name-list">
<table summary="Constants">
<% sections["constants"].each do |constants| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= constants["name"] %></td>
<td>=</td>
<td class="context-item-value"><%= constants["value"] %></td>
<% if constants["desc"] then %>
<td>&nbsp;</td>
<td class="context-item-desc"><%= constants["desc"] %></td>
<% end %>
</tr>
<% end %><%# sections["constants"] %>
</table>
</div>
</div>
<% end
if sections["aliases"] then %>
<div id="aliases-list">
<h3 class="section-bar">External Aliases</h3>
<div class="name-list">
<table summary="aliases">
<% sections["aliases"].each do |aliases| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= aliases["old_name"] %></td>
<td>-&gt;</td>
<td class="context-item-value"><%= aliases["new_name"] %></td>
</tr>
<% if aliases["desc"] then %>
<tr class="top-aligned-row context-row">
<td>&nbsp;</td>
<td colspan="2" class="context-item-desc"><%= aliases["desc"] %></td>
</tr>
<% end
end %><%# sections["aliases"] %>
</table>
</div>
</div>
<% end %>
<% if sections["attributes"] then %>
<div id="attribute-list">
<h3 class="section-bar">Attributes</h3>
<div class="name-list">
<table>
<% sections["attributes"].each do |attribute| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= attribute["name"] %></td>
<% if attribute["rw"] then %>
<td class="context-item-value">&nbsp;[<%= attribute["rw"] %>]&nbsp;</td>
<% end
unless attribute["rw"] then %>
<td class="context-item-value">&nbsp;&nbsp;</td>
<% end %>
<td class="context-item-desc"><%= attribute["a_desc"] %></td>
</tr>
<% end %><%# sections["attributes"] %>
</table>
</div>
</div>
<% end %>
<!-- if method_list -->
<% if sections["method_list"] then %>
<div id="methods">
<% sections["method_list"].each do |method_list|
if method_list["methods"] then %>
<h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3>
<% method_list["methods"].each do |methods| %>
<div id="method-<%= methods["aref"] %>" class="method-detail">
<a name="<%= methods["aref"] %>"></a>
<div class="method-heading">
<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="Code" class="method-signature"
onclick="popupCode('<%= methods["codeurl"] %>');return false;">
<% end
if methods["sourcecode"] then %>
<a href="#<%= methods["aref"] %>" class="method-signature">
<% end
if methods["callseq"] then %>
<span class="method-name"><%= methods["callseq"] %></span>
<% end
unless methods["callseq"] then %>
<span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span>
<% end
if methods["codeurl"] then %>
</a>
<% end
if methods["sourcecode"] then %>
</a>
<% end %>
</div>
<div class="method-description">
<% if methods["m_desc"] then %>
<%= methods["m_desc"] %>
<% end
if methods["sourcecode"] then %>
<p><a class="source-toggle" href="#"
onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p>
<div class="method-source-code" id="<%= methods["aref"] %>-source">
<pre>
<%= methods["sourcecode"] %>
</pre>
</div>
<% end %>
</div>
</div>
<% end %><%# method_list["methods"] %><%
end
end %><%# sections["method_list"] %>
</div>
<% end %>
<% end %><%# values["sections"] %>
EOF
#####################################################################
### B O D Y T E M P L A T E
#####################################################################
BODY = HEADER + %{
<%= template_include %> <!-- banner header -->
<div id="bodyContent">
} + METHOD_LIST + %{
</div>
} + FOOTER
#####################################################################
### S O U R C E C O D E T E M P L A T E
#####################################################################
SRC_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body class="standalone-code">
<pre><%= values["code"] %></pre>
</body>
</html>
EOF
#####################################################################
### I N D E X F I L E T E M P L A T E S
#####################################################################
FR_INDEX_BODY = %{<%= template_include %>}
FILE_INDEX = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<!--
<%= values["title"] %>
-->
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" />
<base target="docwin" />
</head>
<body>
<div class="index">
<h1 class="section-bar"><%= values["list_title"] %></h1>
<div id="index-entries">
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</div>
</div>
</body>
</html>
EOF
CLASS_INDEX = FILE_INDEX
METHOD_INDEX = FILE_INDEX
INDEX = XHTML_FRAME_PREAMBLE + HTML_ELEMENT + <<-EOF
<!--
<%= values["title"] %>
-->
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<frameset rows="20%, 80%">
<frameset cols="25%,35%,45%">
<frame src="fr_file_index.html" title="Files" name="Files" />
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
<frame src="<%= values["initial_page"] %>" name="docwin" />
</frameset>
</html>
EOF
end

View file

@ -1,151 +0,0 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::KILMER
FONTS = "Verdana, Arial, Helvetica, sans-serif"
CENTRAL_STYLE = <<-EOF
body,td,p { font-family: <%= values["fonts"] %>;
color: #000040;
}
.attr-rw { font-size: xx-small; color: #444488 }
.title-row { background-color: #CCCCFF;
color: #000010;
}
.big-title-font {
color: black;
font-weight: bold;
font-family: <%= values["fonts"] %>;
font-size: large;
height: 60px;
padding: 10px 3px 10px 3px;
}
.small-title-font { color: black;
font-family: <%= values["fonts"] %>;
font-size:10; }
.aqua { color: black }
#diagram img {
border: 0;
}
.method-name, .attr-name {
font-family: font-family: <%= values["fonts"] %>;
font-weight: bold;
font-size: small;
margin-left: 20px;
color: #000033;
}
.tablesubtitle, .tablesubsubtitle {
width: 100%;
margin-top: 1ex;
margin-bottom: .5ex;
padding: 5px 0px 5px 3px;
font-size: large;
color: black;
background-color: #CCCCFF;
border: thin;
}
.name-list {
margin-left: 5px;
margin-bottom: 2ex;
line-height: 105%;
}
.description {
margin-left: 5px;
margin-bottom: 2ex;
line-height: 105%;
font-size: small;
}
.methodtitle {
font-size: small;
font-weight: bold;
text-decoration: none;
color: #000033;
background: #ccc;
}
.srclink {
font-size: small;
font-weight: bold;
text-decoration: none;
color: #0000DD;
background-color: white;
}
.srcbut { float: right }
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; }
.ruby-ivar { color: #2233dd; }
.ruby-keyword { color: #3333FF; font-weight: bold }
.ruby-node { color: #777777; }
.ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic }
EOF
INDEX_STYLE = <<-EOF
body {
background-color: #ddddff;
font-family: #{FONTS};
font-size: 11px;
font-style: normal;
line-height: 14px;
color: #000040;
}
div.banner {
background: #0000aa;
color: white;
padding: 1;
margin: 0;
font-size: 90%;
font-weight: bold;
line-height: 1.1;
text-align: center;
width: 100%;
}
EOF
FACTORY = RDoc::Generator::HTML::
KilmerFactory.new(:central_css => CENTRAL_STYLE,
:index_css => INDEX_STYLE)
STYLE = FACTORY.get_STYLE()
METHOD_LIST = FACTORY.get_METHOD_LIST()
BODY = FACTORY.get_BODY()
FILE_PAGE = FACTORY.get_FILE_PAGE()
CLASS_PAGE = FACTORY.get_CLASS_PAGE()
SRC_PAGE = FACTORY.get_SRC_PAGE()
FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
FILE_INDEX = FACTORY.get_FILE_INDEX()
CLASS_INDEX = FACTORY.get_CLASS_INDEX()
METHOD_INDEX = FACTORY.get_METHOD_INDEX()
INDEX = FACTORY.get_INDEX()
def self.write_extra_pages(values)
FACTORY.write_extra_pages(values)
end
end

View file

@ -1,427 +0,0 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/common'
#
# This class generates Kilmer-style templates. Right now,
# rdoc is shipped with two such templates:
# * kilmer
# * hefss
#
# Kilmer-style templates use frames. The left side of the page has
# three frames stacked on top of each other: one lists
# files, one lists classes, and one lists methods. If source code
# is not inlined, an additional frame runs across the bottom of
# the page and will be used to display method source code.
# The central (and largest frame) display class and file
# pages.
#
# The constructor of this class accepts a Hash containing stylistic
# attributes. Then, a get_BLAH instance method of this class returns a
# value for the template's BLAH constant. get_BODY, for instance, returns
# the value of the template's BODY constant.
#
class RDoc::Generator::HTML::KilmerFactory
include RDoc::Generator::HTML::Common
#
# The contents of the stylesheet that should be used for the
# central frame (for the class and file pages).
#
# This must be specified in the Hash passed to the constructor.
#
attr_reader :central_css
#
# The contents of the stylesheet that should be used for the
# index pages.
#
# This must be specified in the Hash passed to the constructor.
#
attr_reader :index_css
#
# The heading that should be displayed before listing methods.
#
# If not supplied, this defaults to "Methods".
#
attr_reader :method_list_heading
#
# The heading that should be displayed before listing classes and
# modules.
#
# If not supplied, this defaults to "Classes and Modules".
#
attr_reader :class_and_module_list_heading
#
# The heading that should be displayed before listing attributes.
#
# If not supplied, this defaults to "Attributes".
#
attr_reader :attribute_list_heading
#
# ====Description:
# This method constructs a KilmerFactory instance, which
# can be used to build Kilmer-style template classes.
# The +style_attributes+ argument is a Hash that contains the
# values of the classes attributes (Symbols mapped to Strings).
#
# ====Parameters:
# [style_attributes]
# A Hash describing the appearance of the Kilmer-style.
#
def initialize(style_attributes)
@central_css = style_attributes[:central_css]
if(!@central_css)
raise ArgumentError, "did not specify a value for :central_css"
end
@index_css = style_attributes[:index_css]
if(!@index_css)
raise ArgumentError, "did not specify a value for :index_css"
end
@method_list_heading = style_attributes[:method_list_heading]
if(!@method_list_heading)
@method_list_heading = "Methods"
end
@class_and_module_list_heading = style_attributes[:class_and_module_list_heading]
if(!@class_and_module_list_heading)
@class_and_module_list_heading = "Classes and Modules"
end
@attribute_list_heading = style_attributes[:attribute_list_heading]
if(!@attribute_list_heading)
@attribute_list_heading = "Attributes"
end
end
def get_STYLE
return @central_css
end
def get_METHOD_LIST
return %{
<% if values["diagram"] then %>
<div id="diagram">
<table width="100%"><tr><td align="center">
<%= values["diagram"] %>
</td></tr></table>
</div>
<% end %>
<% if values["description"] then %>
<div class="description"><%= values["description"] %></div>
<% end %>
<% if values["requires"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Required files</td></tr>
</table><br />
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
<% end %><%# values["requires"] %>
</div>
<% end %>
<% if values["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@method_list_heading}</td></tr>
</table><br />
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>,
<% end %><%# values["methods"] %>
</div>
<% end %>
<% if values["includes"] then %>
<div class="tablesubsubtitle">Included modules</div><br />
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
<% end %>
<% values["sections"].each do |sections| %>
<div id="section">
<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
<% end %>
<% end %>
<% if sections["attributes"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@attribute_list_heading}</td></tr>
</table><br />
<table cellspacing="5">
<% sections["attributes"].each do |attributes| %>
<tr valign="top">
<% if attributes["rw"] then %>
<td align="center" class="attr-rw">&nbsp;[<%= attributes["rw"] %>]&nbsp;</td>
<% end %>
<% unless attributes["rw"] then %>
<td></td>
<% end %>
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
<% end %><%# sections["attributes"] %>
</table>
<% end %>
<% if sections["classlist"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@class_and_module_list_heading}</td></tr>
</table><br />
<%= sections["classlist"] %><br />
<% end %>
<% if sections["method_list"] then %>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr>
</table>
<% method_list["methods"].each do |methods| %>
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tr><td class="methodtitle">
<a name="<%= methods["aref"] %>">
<% if methods["callseq"] then %>
<b><%= methods["callseq"] %></b>
<% end %>
<% unless methods["callseq"] then %>
<b><%= methods["name"] %></b><%= methods["params"] %>
<% end %>
</a>
<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a>
<% end %>
</td></tr>
</table>
<% if methods["m_desc"] then %>
<div class="description">
<%= methods["m_desc"] %>
</div>
<% end %>
<% if methods["aka"] then %>
<div class="aka">
This method is also aliased as
<% methods["aka"].each do |aka| %>
<a href="<%= methods["aref"] %>"><%= methods["name"] %></a>
<% end %><%# methods["aka"] %>
</div>
<% end %>
<% if methods["sourcecode"] then %>
<pre class="source">
<%= methods["sourcecode"] %>
</pre>
<% end %>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
<% end %>
<% end %><%# values["sections"] %>
</div>
}
end
def get_BODY
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript">
<!--
function popCode(url) {
parent.frames.source.location = url
}
//-->
</script>
</head>
<body>
<div class="bodyContent">
<%= template_include %> <!-- banner header -->
#{get_METHOD_LIST()}
</div>
</body>
</html>
}
end
def get_FILE_PAGE
return %{
<table width="100%">
<tr class="title-row">
<td><table width="100%"><tr>
<td class="big-title-font" colspan="2">File<br /><%= values["short_name"] %></td>
<td align="right"><table cellspacing="0" cellpadding="2">
<tr>
<td class="small-title-font">Path:</td>
<td class="small-title-font"><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr>
<td class="small-title-font">Modified:</td>
<td class="small-title-font"><%= values["dtm_modified"] %></td>
</tr>
</table>
</td></tr></table></td>
</tr>
</table><br />
}
end
def get_CLASS_PAGE
return %{
<table width="100%" border="0" cellspacing="0">
<tr class="title-row">
<td class="big-title-font">
<%= values["classmod"] %><br /><%= values["full_name"] %>
</td>
<td align="right">
<table cellspacing="0" cellpadding="2">
<tr valign="top">
<td class="small-title-font">In:</td>
<td class="small-title-font">
<% values["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr>
<td class="small-title-font">Parent:</td>
<td class="small-title-font">
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>" class="cyan">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</td>
</tr>
</table><br />
}
end
def get_SRC_PAGE
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head><title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body>
<pre><%= values["code"] %></pre>
</body>
</html>
}
end
def get_FR_INDEX_BODY
return %{<%= template_include %>}
end
def get_FILE_INDEX
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<style type="text/css">
<!--
#{@index_css}
-->
</style>
<base target="docwin" />
</head>
<body>
<div class="index">
<div class="banner"><%= values["list_title"] %></div>
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</div>
</body></html>
}
end
def get_CLASS_INDEX
return get_FILE_INDEX
end
def get_METHOD_INDEX
return get_FILE_INDEX
end
def get_INDEX
return XHTML_FRAME_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<frameset cols="20%,*">
<frameset rows="15%,35%,50%">
<frame src="fr_file_index.html" title="Files" name="Files" />
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
<% if values["inline_source"] then %>
<frame src="<%= values["initial_page"] %>" name="docwin" />
<% end %>
<% unless values["inline_source"] then %>
<frameset rows="80%,20%">
<frame src="<%= values["initial_page"] %>" name="docwin" />
<frame src="blank.html" name="source" />
</frameset>
<% end %>
</frameset>
</html>
}
end
def get_BLANK
# This will be displayed in the source code frame before
# any source code has been selected.
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title>Source Code Frame <%= values["title_suffix"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body>
</body>
</html>
}
end
def write_extra_pages(values)
template = RDoc::TemplatePage.new(get_BLANK())
File.open("blank.html", "w") { |f| template.write_html_on(f, values) }
end
end

View file

@ -1,122 +0,0 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/common'
module RDoc::Generator::HTML::ONE_PAGE_HTML
include RDoc::Generator::HTML::Common
CONTENTS_XML = <<-EOF
<% if defined? classes and classes["description"] then %>
<%= classes["description"] %>
<% end %>
<% if defined? files and files["requires"] then %>
<h4>Requires:</h4>
<ul>
<% files["requires"].each do |requires| %>
<% if requires["aref"] then %>
<li><a href="<%= requires["aref"] %>"><%= requires["name"] %></a></li>
<% end %>
<% unless requires["aref"] then %>
<li><%= requires["name"] %></li>
<% end %>
<% end %><%# files["requires"] %>
</ul>
<% end %>
<% if defined? classes and classes["includes"] then %>
<h4>Includes</h4>
<ul>
<% classes["includes"].each do |includes| %>
<% if includes["aref"] then %>
<li><a href="<%= includes["aref"] %>"><%= includes["name"] %></a></li>
<% end %>
<% unless includes["aref"] then %>
<li><%= includes["name"] %></li>
<% end %>
<% end %><%# classes["includes"] %>
</ul>
<% end %>
<% if defined? classes and classes["sections"] then %>
<% classes["sections"].each do |sections| %>
<% if sections["attributes"] then %>
<h4>Attributes</h4>
<table>
<% sections["attributes"].each do |attributes| %>
<tr><td><%= attributes["name"] %></td><td><%= attributes["rw"] %></td><td><%= attributes["a_desc"] %></td></tr>
<% end %><%# sections["attributes"] %>
</table>
<% end %>
<% if sections["method_list"] then %>
<h3>Methods</h3>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<% method_list["methods"].each do |methods| %>
<h4><%= methods["type"] %> <%= methods["category"] %> method:
<% if methods["callseq"] then %>
<a name="<%= methods["aref"] %>"><%= methods["callseq"] %></a>
<% end %>
<% unless methods["callseq"] then %>
<a name="<%= methods["aref"] %>"><%= methods["name"] %><%= methods["params"] %></a></h4>
<% end %>
<% if methods["m_desc"] then %>
<%= methods["m_desc"] %>
<% end %>
<% if methods["sourcecode"] then %>
<blockquote><pre>
<%= methods["sourcecode"] %>
</pre></blockquote>
<% end %>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
<% end %>
<% end %><%# classes["sections"] %>
<% end %>
EOF
ONE_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<body>
<% values["files"].each do |files| %>
<h2>File: <a name="<%= files["href"] %>"><%= files["short_name"] %></a></h2>
<table>
<tr><td>Path:</td><td><%= files["full_path"] %></td></tr>
<tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr>
</table>
} + CONTENTS_XML + %{
<% end %><%# values["files"] %>
<% if values["classes"] then %>
<h2>Classes</h2>
<% values["classes"].each do |classes| %>
<% if classes["parent"] then %>
<h3><%= classes["classmod"] %> <a name="<%= classes["href"] %>"><%= classes["full_name"] %></a> &lt; <%= href classes["par_url"], classes["parent"] %></h3>
<% end %>
<% unless classes["parent"] then %>
<h3><%= classes["classmod"] %> <%= classes["full_name"] %></h3>
<% end %>
<% if classes["infiles"] then %>
(in files
<% classes["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
<% end %><%# classes["infiles"] %>
)
<% end %>
} + CONTENTS_XML + %{
<% end %><%# values["classes"] %>
<% end %>
</body>
</html>
}
end

View file

@ -0,0 +1,171 @@
require 'rdoc/text'
require 'rdoc/code_objects'
require 'rdoc/generator'
require 'rdoc/markup/to_html_crossref'
##
# Handle common RDoc::Markup tasks for various CodeObjects
module RDoc::Generator::Markup
##
# Generates a relative URL from this object's path to +target_path+
def aref_to(target_path)
RDoc::Markup::ToHtml.gen_relative_url path, target_path
end
##
# Generates a relative URL from +from_path+ to this object's path
def as_href(from_path)
RDoc::Markup::ToHtml.gen_relative_url from_path, path
end
##
# Handy wrapper for marking up this object's comment
def description
markup @comment
end
##
# Creates an RDoc::Markup::ToHtmlCrossref formatter
def formatter
return @formatter if defined? @formatter
show_hash = RDoc::RDoc.current.options.show_hash
this = RDoc::Context === self ? self : @parent
@formatter = RDoc::Markup::ToHtmlCrossref.new this.path, this, show_hash
end
##
# Build a webcvs URL starting for the given +url+ with +full_path+ appended
# as the destination path. If +url+ contains '%s' +full_path+ will be
# sprintf'd into +url+ instead.
def cvs_url(url, full_path)
if /%s/ =~ url then
sprintf url, full_path
else
url + full_path
end
end
end
class RDoc::AnyMethod
include RDoc::Generator::Markup
##
# Prepend +src+ with line numbers. Relies on the first line of a source
# code listing having:
#
# # File xxxxx, line dddd
def add_line_numbers(src)
if src =~ /\A.*, line (\d+)/ then
first = $1.to_i - 1
last = first + src.count("\n")
size = last.to_s.length
line = first
src.gsub!(/^/) do
res = if line == first then
" " * (size + 2)
else
"%2$*1$d: " % [size, line]
end
line += 1
res
end
end
end
##
# Turns the method's token stream into HTML
def markup_code
return '' unless @token_stream
src = ""
@token_stream.each do |t|
next unless t
# style = STYLE_MAP[t.class]
style = case t
when RDoc::RubyToken::TkCONSTANT then "ruby-constant"
when RDoc::RubyToken::TkKW then "ruby-keyword kw"
when RDoc::RubyToken::TkIVAR then "ruby-ivar"
when RDoc::RubyToken::TkOp then "ruby-operator"
when RDoc::RubyToken::TkId then "ruby-identifier"
when RDoc::RubyToken::TkNode then "ruby-node"
when RDoc::RubyToken::TkCOMMENT then "ruby-comment cmt"
when RDoc::RubyToken::TkREGEXP then "ruby-regexp re"
when RDoc::RubyToken::TkSTRING then "ruby-value str"
when RDoc::RubyToken::TkVal then "ruby-value"
else
nil
end
text = CGI.escapeHTML t.text
if style
src << "<span class=\"#{style}\">#{text}</span>"
else
src << text
end
end
add_line_numbers src
src
end
end
class RDoc::Attr
include RDoc::Generator::Markup
end
class RDoc::Constant
include RDoc::Generator::Markup
end
class RDoc::Context
include RDoc::Generator::Markup
end
class RDoc::Context::Section
include RDoc::Generator::Markup
end
class RDoc::TopLevel
##
# Returns a URL for this source file on some web repository. Use the -W
# command line option to set.
def cvs_url
url = RDoc::RDoc.current.options.webcvs
if /%s/ =~ url then
url % @absolute_name
else
url + @absolute_name
end
end
end

View file

@ -1,225 +1,79 @@
require 'rdoc/generator'
require 'rdoc/markup/to_flow'
require 'rdoc/ri'
require 'rdoc/ri/cache'
require 'rdoc/ri/reader'
require 'rdoc/ri/writer'
require 'rdoc/ri/descriptions'
##
# Generates ri data files
class RDoc::Generator::RI
##
# Generator may need to return specific subclasses depending on the
# options they are passed. Because of this we create them using a factory
RDoc::RDoc.add_generator self
def self.for(options)
new(options)
def self.for options
new options
end
##
# Set up a new ri generator
def initialize(options) #:not-new:
@options = options
@ri_writer = RDoc::RI::Writer.new "."
@markup = RDoc::Markup.new
@to_flow = RDoc::Markup::ToFlow.new
@generated = {}
def initialize options #:not-new:
@options = options
@store = RDoc::RI::Store.new '.'
@old_siginfo = nil
@current = nil
end
##
# Build the initial indices and output objects based on an array of
# TopLevel objects containing the extracted information.
# Build the initial indices and output objects based on an array of TopLevel
# objects containing the extracted information.
def generate(toplevels)
RDoc::TopLevel.all_classes_and_modules.each do |cls|
process_class cls
end
end
def generate top_levels
install_siginfo_handler
def process_class(from_class)
generate_class_info(from_class)
RDoc::TopLevel.all_classes_and_modules.each do |klass|
@current = "#{klass.class}: #{klass.full_name}"
# now recurse into this class' constituent classes
from_class.each_classmodule do |mod|
process_class(mod)
end
end
@store.save_class klass
def generate_class_info(cls)
case cls
when RDoc::NormalModule then
cls_desc = RDoc::RI::ModuleDescription.new
else
cls_desc = RDoc::RI::ClassDescription.new
cls_desc.superclass = cls.superclass
end
klass.each_method do |method|
@current = "#{method.class}: #{method.full_name}"
@store.save_method klass, method
end
cls_desc.name = cls.name
cls_desc.full_name = cls.full_name
cls_desc.comment = markup(cls.comment)
cls_desc.attributes = cls.attributes.sort.map do |a|
RDoc::RI::Attribute.new(a.name, a.rw, markup(a.comment))
end
cls_desc.constants = cls.constants.map do |c|
RDoc::RI::Constant.new(c.name, c.value, markup(c.comment))
end
cls_desc.includes = cls.includes.map do |i|
RDoc::RI::IncludedModule.new(i.name)
end
class_methods, instance_methods = method_list(cls)
cls_desc.class_methods = class_methods.map do |m|
RDoc::RI::MethodSummary.new(m.name)
end
cls_desc.instance_methods = instance_methods.map do |m|
RDoc::RI::MethodSummary.new(m.name)
end
update_or_replace(cls_desc)
class_methods.each do |m|
generate_method_info(cls_desc, m)
end
instance_methods.each do |m|
generate_method_info(cls_desc, m)
end
end
def generate_method_info(cls_desc, method)
meth_desc = RDoc::RI::MethodDescription.new
meth_desc.name = method.name
meth_desc.full_name = cls_desc.full_name
if method.singleton
meth_desc.full_name += "::"
else
meth_desc.full_name += "#"
end
meth_desc.full_name << method.name
meth_desc.comment = markup(method.comment)
meth_desc.params = params_of(method)
meth_desc.visibility = method.visibility.to_s
meth_desc.is_singleton = method.singleton
meth_desc.block_params = method.block_params
meth_desc.aliases = method.aliases.map do |a|
RDoc::RI::AliasName.new(a.name)
end
@ri_writer.add_method(cls_desc, meth_desc)
end
private
##
# Returns a list of class and instance methods that we'll be documenting
def method_list(cls)
list = cls.method_list
unless @options.show_all
list = list.find_all do |m|
m.visibility == :public || m.visibility == :protected || m.force_documentation
klass.each_attribute do |attribute|
@store.save_method klass, attribute
end
end
c = []
i = []
list.sort.each do |m|
if m.singleton
c << m
else
i << m
end
end
return c,i
end
@current = 'saving cache'
def params_of(method)
if method.call_seq
method.call_seq
else
params = method.params || ""
@store.save_cache
p = params.gsub(/\s*\#.*/, '')
p = p.tr("\n", " ").squeeze(" ")
p = "(" + p + ")" unless p[0] == ?(
ensure
@current = nil
if (block = method.block_params)
block.gsub!(/\s*\#.*/, '')
block = block.tr("\n", " ").squeeze(" ")
if block[0] == ?(
block.sub!(/^\(/, '').sub!(/\)/, '')
end
p << " {|#{block.strip}| ...}"
end
p
end
end
def markup(comment)
return nil if !comment || comment.empty?
# Convert leading comment markers to spaces, but only
# if all non-blank lines have them
if comment =~ /^(?>\s*)[^\#]/
content = comment
else
content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
end
@markup.convert(content, @to_flow)
remove_siginfo_handler
end
##
# By default we replace existing classes with the same name. If the
# --merge option was given, we instead merge this definition into an
# existing class. We add our methods, aliases, etc to that class, but do
# not change the class's description.
# Installs a siginfo handler that prints the current filename.
def update_or_replace(cls_desc)
old_cls = nil
def install_siginfo_handler
return unless Signal.list.key? 'INFO'
if @options.merge
rdr = RDoc::RI::Reader.new RDoc::RI::Cache.new(@options.op_dir)
namespace = rdr.top_level_namespace
namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
if namespace.empty?
$stderr.puts "You asked me to merge this source into existing "
$stderr.puts "documentation. This file references a class or "
$stderr.puts "module called #{cls_desc.name} which I don't"
$stderr.puts "have existing documentation for."
$stderr.puts
$stderr.puts "Perhaps you need to generate its documentation first"
exit 1
else
old_cls = namespace[0]
end
@old_siginfo = trap 'INFO' do
puts @current if @current
end
end
prev_cls = @generated[cls_desc.full_name]
##
# Removes a siginfo handler and replaces the previous
if old_cls and not prev_cls then
old_desc = rdr.get_class old_cls
cls_desc.merge_in old_desc
end
def remove_siginfo_handler
return unless Signal.list.key? 'INFO'
if prev_cls then
cls_desc.merge_in prev_cls
end
handler = @old_siginfo || 'DEFAULT'
@generated[cls_desc.full_name] = cls_desc
@ri_writer.remove_class cls_desc
@ri_writer.add_class cls_desc
trap 'INFO', handler
end
end

View file

@ -0,0 +1,297 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
<title><%= klass.type.capitalize %>: <%= klass.full_name %></title>
<link rel="stylesheet" href="<%= rel_prefix %>/rdoc.css" type="text/css" media="screen" />
<script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
charset="utf-8"></script>
</head>
<body class="<%= klass.type %>">
<div id="metadata">
<div id="home-metadata">
<div id="home-section" class="section">
<h3 class="section-header">
<a href="<%= rel_prefix %>/index.html">Home</a>
<a href="<%= rel_prefix %>/index.html#classes">Classes</a>
<a href="<%= rel_prefix %>/index.html#methods">Methods</a>
</h3>
</div>
</div>
<div id="file-metadata">
<div id="file-list-section" class="section">
<h3 class="section-header">In Files</h3>
<div class="section-body">
<ul>
<% klass.in_files.each do |tl| %>
<li><a href="<%= rel_prefix %>/<%= h tl.path %>?TB_iframe=true&amp;height=550&amp;width=785"
class="thickbox" title="<%= h tl.absolute_name %>"><%= h tl.absolute_name %></a></li>
<% end %>
</ul>
</div>
</div>
<% if !svninfo.empty? %>
<div id="file-svninfo-section" class="section">
<h3 class="section-header">Subversion Info</h3>
<div class="section-body">
<dl class="svninfo">
<dt>Rev</dt>
<dd><%= svninfo[:rev] %></dd>
<dt>Last Checked In</dt>
<dd><%= svninfo[:commitdate].strftime('%Y-%m-%d %H:%M:%S') %>
(<%= svninfo[:commitdelta] %> ago)</dd>
<dt>Checked in by</dt>
<dd><%= svninfo[:committer] %></dd>
</dl>
</div>
</div>
<% end %>
</div>
<div id="class-metadata">
<!-- Parent Class -->
<% if klass.type == 'class' %>
<div id="parent-class-section" class="section">
<h3 class="section-header">Parent</h3>
<% unless String === klass.superclass %>
<p class="link"><a href="<%= klass.aref_to klass.superclass.path %>"><%= klass.superclass.full_name %></a></p>
<% else %>
<p class="link"><%= klass.superclass %></p>
<% end %>
</div>
<% end %>
<!-- Namespace Contents -->
<% unless klass.classes_and_modules.empty? %>
<div id="namespace-list-section" class="section">
<h3 class="section-header">Namespace</h3>
<ul class="link-list">
<% (klass.modules.sort + klass.classes.sort).each do |mod| %>
<li><span class="type"><%= mod.type.upcase %></span> <a href="<%= klass.aref_to mod.path %>"><%= mod.full_name %></a></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Method Quickref -->
<% unless klass.method_list.empty? %>
<div id="method-list-section" class="section">
<h3 class="section-header">Methods</h3>
<ul class="link-list">
<% klass.each_method do |meth| %>
<li><a href="#<%= meth.aref %>"><%= meth.singleton ? '::' : '#' %><%= meth.name %></a></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Included Modules -->
<% unless klass.includes.empty? %>
<div id="includes-section" class="section">
<h3 class="section-header">Included Modules</h3>
<ul class="link-list">
<% klass.each_include do |inc| %>
<% unless String === inc.module %>
<li><a class="include" href="<%= klass.aref_to inc.module.path %>"><%= inc.module.full_name %></a></li>
<% else %>
<li><span class="include"><%= inc.name %></span></li>
<% end %>
<% end %>
</ul>
</div>
<% end %>
</div>
<div id="project-metadata">
<% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
<% unless simple_files.empty? then %>
<div id="fileindex-section" class="section project-section">
<h3 class="section-header">Files</h3>
<ul>
<% simple_files.each do |file| %>
<li class="file"><a href="<%= rel_prefix %>/<%= file.path %>"><%= h file.base_name %></a></li>
<% end %>
</ul>
</div>
<% end %>
<div id="classindex-section" class="section project-section">
<h3 class="section-header">Class Index
<span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
height="16" width="16" alt="[+]"
title="show/hide quicksearch" /></span></h3>
<form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
<fieldset>
<legend>Quicksearch</legend>
<input type="text" name="quicksearch" value=""
class="quicksearch-field" />
</fieldset>
</form>
<ul class="link-list">
<% @modsort.each do |index_klass| %>
<li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
<% end %>
</ul>
<div id="no-class-search-results" style="display: none;">No matching classes.</div>
</div>
<% if $DEBUG_RDOC %>
<div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
alt="toggle debugging" height="16" width="16" /></div>
<% end %>
</div>
</div>
<div id="documentation">
<h1 class="<%= klass.type %>"><%= klass.full_name %></h1>
<div id="description">
<%= klass.description %>
</div>
<!-- Constants -->
<% unless klass.constants.empty? %>
<div id="constants-list" class="section">
<h3 class="section-header">Constants</h3>
<dl>
<% klass.each_constant do |const| %>
<dt><a name="<%= const.name %>"><%= const.name %></a></dt>
<% if const.comment %>
<dd class="description"><%= const.description.strip %></dd>
<% else %>
<dd class="description missing-docs">(Not documented)</dd>
<% end %>
<% end %>
</dl>
</div>
<% end %>
<!-- Attributes -->
<% unless klass.attributes.empty? %>
<div id="attribute-method-details" class="method-section section">
<h3 class="section-header">Attributes</h3>
<% klass.each_attribute do |attrib| %>
<div id="<%= attrib.html_name %>-attribute-method" class="method-detail">
<a name="<%= h attrib.name %>"></a>
<% if attrib.rw =~ /w/i %>
<a name="<%= h attrib.name %>="></a>
<% end %>
<div class="method-heading attribute-method-heading">
<span class="method-name"><%= h attrib.name %></span><span
class="attribute-access-type">[<%= attrib.rw %>]</span>
</div>
<div class="method-description">
<% if attrib.comment %>
<%= attrib.description.strip %>
<% else %>
<p class="missing-docs">(Not documented)</p>
<% end %>
</div>
</div>
<% end %>
</div>
<% end %>
<!-- Methods -->
<% klass.methods_by_type.each do |type, visibilities|
next if visibilities.empty?
visibilities.each do |visibility, methods|
next if methods.empty? %>
<div id="<%= visibility %>-<%= type %>-method-details" class="method-section section">
<h3 class="section-header"><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
<% methods.each do |method| %>
<div id="<%= method.html_name %>-method" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
<a name="<%= h method.aref %>"></a>
<div class="method-heading">
<% if method.call_seq %>
<span class="method-callseq"><%= method.call_seq.strip.gsub(/->/, '&rarr;').gsub( /^\w.+\./m, '') %></span>
<span class="method-click-advice">click to toggle source</span>
<% else %>
<span class="method-name"><%= h method.name %></span><span
class="method-args"><%= method.params %></span>
<span class="method-click-advice">click to toggle source</span>
<% end %>
</div>
<div class="method-description">
<% if method.comment %>
<%= method.description.strip %>
<% else %>
<p class="missing-docs">(Not documented)</p>
<% end %>
<% if method.token_stream %>
<div class="method-source-code"
id="<%= method.html_name %>-source">
<pre>
<%= method.markup_code %>
</pre>
</div>
<% end %>
</div>
<% unless method.aliases.empty? %>
<div class="aliases">
Also aliased as: <%= method.aliases.map do |aka|
%{<a href="#{ klass.aref_to aka.path}">#{h aka.name}</a>}
end.join(", ") %>
</div>
<% end %>
<% if method.is_alias_for then %>
<div class="aliases">
Alias for: <a href="<%= klass.aref_to method.is_alias_for.path %>"><%= h method.is_alias_for.name %></a>
</div>
<% end %>
</div>
<% end %>
</div>
<% end
end %>
</div>
<div id="rdoc-debugging-section-dump" class="debugging-section">
<% if $DEBUG_RDOC
require 'pp' %>
<pre><%= h PP.pp(klass, _erbout) %></pre>
</div>
<% else %>
<p>Disabled; run with --debug to generate this.</p>
<% end %>
</div>
<div id="validator-badges">
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
<p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
</div>
</body>
</html>

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
<title>File: <%= file.base_name %> [<%= @options.title %>]</title>
<link type="text/css" media="screen" href="<%= rel_prefix %>/rdoc.css" rel="stylesheet" />
<script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
charset="utf-8"></script>
<script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
charset="utf-8"></script>
</head>
<% if file.parser == RDoc::Parser::Simple %>
<body class="file">
<div id="metadata">
<div id="home-metadata">
<div id="home-section" class="section">
<h3 class="section-header">
<a href="<%= rel_prefix %>/index.html">Home</a>
<a href="<%= rel_prefix %>/index.html#classes">Classes</a>
<a href="<%= rel_prefix %>/index.html#methods">Methods</a>
</h3>
</div>
</div>
<div id="project-metadata">
<% simple_files = @files.select { |f| f.parser == RDoc::Parser::Simple } %>
<% unless simple_files.empty? then %>
<div id="fileindex-section" class="section project-section">
<h3 class="section-header">Files</h3>
<ul>
<% simple_files.each do |f| %>
<li class="file"><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.base_name %></a></li>
<% end %>
</ul>
</div>
<% end %>
<div id="classindex-section" class="section project-section">
<h3 class="section-header">Class Index
<span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
height="16" width="16" alt="[+]"
title="show/hide quicksearch" /></span></h3>
<form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
<fieldset>
<legend>Quicksearch</legend>
<input type="text" name="quicksearch" value=""
class="quicksearch-field" />
</fieldset>
</form>
<ul class="link-list">
<% @modsort.each do |index_klass| %>
<li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
<% end %>
</ul>
<div id="no-class-search-results" style="display: none;">No matching classes.</div>
</div>
<% if $DEBUG_RDOC %>
<div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
alt="toggle debugging" height="16" width="16" /></div>
<% end %>
</div>
</div>
<div id="documentation">
<%= file.description %>
</div>
<div id="validator-badges">
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
<p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
</div>
</body>
<% else %>
<body class="file file-popup">
<div id="metadata">
<dl>
<dt class="modified-date">Last Modified</dt>
<dd class="modified-date"><%= file.last_modified %></dd>
<% if file.requires %>
<dt class="requires">Requires</dt>
<dd class="requires">
<ul>
<% file.requires.each do |require| %>
<li><%= require.name %></li>
<% end %>
</ul>
</dd>
<% end %>
<% if @options.webcvs %>
<dt class="scs-url">Trac URL</dt>
<dd class="scs-url"><a target="_top"
href="<%= file.cvs_url %>"><%= file.cvs_url %></a></dd>
<% end %>
</dl>
</div>
<div id="documentation">
<% if file.comment %>
<div class="description">
<h2>Description</h2>
<%= file.description %>
</div>
<% end %>
</div>
</body>
<% end %>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
<title><%= h @options.title %></title>
<link type="text/css" media="screen" href="rdoc.css" rel="stylesheet" />
<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="js/thickbox-compressed.js" type="text/javascript" charset="utf-8"></script>
<script src="js/quicksearch.js" type="text/javascript" charset="utf-8"></script>
<script src="js/darkfish.js" type="text/javascript" charset="utf-8"></script>
</head>
<body class="indexpage">
<% $stderr.sync = true %>
<h1><%= h @options.title %></h1>
<% if @options.main_page && main_page = @files.find { |f| f.full_name == @options.main_page } %>
<div id="main">
<%= main_page.description.sub(%r{^\s*<h1.*?/h1>}i, '') %>
</div>
<% else %>
<p>This is the API documentation for '<%= @options.title %>'.</p>
<% end %>
<% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
<% unless simple_files.empty? then %>
<h2>Files</h2>
<ul>
<% simple_files.sort.each do |file| %>
<li class="file"><a href="<%= file.path %>"><%= h file.base_name %></a></li>
<% end %>
</ul>
<% end %>
<h2 id="classes">Classes/Modules</h2>
<ul>
<% @modsort.each do |klass| %>
<li class="<%= klass.type %>"><a href="<%= klass.path %>"><%= klass.full_name %></a></li>
<% end %>
</ul>
<h2 id="methods">Methods</h2>
<ul>
<% RDoc::TopLevel.all_classes_and_modules.map do |mod|
mod.method_list
end.flatten.sort.each do |method| %>
<li><a href="<%= method.path %>"><%= method.pretty_name %> &mdash; <%= method.parent.full_name %></a></li>
<% end %>
</ul>
<div id="validator-badges">
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
<p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
</div>
</body>
</html>

View file

@ -0,0 +1,116 @@
/**
*
* Darkfish Page Functions
* $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $
*
* Author: Michael Granger <mgranger@laika.com>
*
*/
/* Provide console simulation for firebug-less environments */
if (!("console" in window) || !("firebug" in console)) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
};
/**
* Unwrap the first element that matches the given @expr@ from the targets and return them.
*/
$.fn.unwrap = function( expr ) {
return this.each( function() {
$(this).parents( expr ).eq( 0 ).after( this ).remove();
});
};
function showSource( e ) {
var target = e.target;
var codeSections = $(target).
parents('.method-detail').
find('.method-source-code');
$(target).
parents('.method-detail').
find('.method-source-code').
slideToggle();
};
function hookSourceViews() {
$('.method-description,.method-heading').click( showSource );
};
function toggleDebuggingSection() {
$('.debugging-section').slideToggle();
};
function hookDebuggingToggle() {
$('#debugging-toggle img').click( toggleDebuggingSection );
};
function hookQuickSearch() {
$('.quicksearch-field').each( function() {
var searchElems = $(this).parents('.section').find( 'li' );
var toggle = $(this).parents('.section').find('h3 .search-toggle');
// console.debug( "Toggle is: %o", toggle );
var qsbox = $(this).parents('form').get( 0 );
$(this).quicksearch( this, searchElems, {
noSearchResultsIndicator: 'no-class-search-results',
focusOnLoad: false
});
$(toggle).click( function() {
// console.debug( "Toggling qsbox: %o", qsbox );
$(qsbox).toggle();
});
});
};
function highlightTarget( anchor ) {
console.debug( "Highlighting target '%s'.", anchor );
$("a[name=" + anchor + "]").each( function() {
if ( !$(this).parent().parent().hasClass('target-section') ) {
console.debug( "Wrapping the target-section" );
$('div.method-detail').unwrap( 'div.target-section' );
$(this).parent().wrap( '<div class="target-section"></div>' );
} else {
console.debug( "Already wrapped." );
}
});
};
function highlightLocationTarget() {
console.debug( "Location hash: %s", window.location.hash );
if ( ! window.location.hash || window.location.hash.length == 0 ) return;
var anchor = window.location.hash.substring(1);
console.debug( "Found anchor: %s; matching %s", anchor, "a[name=" + anchor + "]" );
highlightTarget( anchor );
};
function highlightClickTarget( event ) {
console.debug( "Highlighting click target for event %o", event.target );
try {
var anchor = $(event.target).attr( 'href' ).substring(1);
console.debug( "Found target anchor: %s", anchor );
highlightTarget( anchor );
} catch ( err ) {
console.error( "Exception while highlighting: %o", err );
};
};
$(document).ready( function() {
hookSourceViews();
hookDebuggingToggle();
hookQuickSearch();
highlightLocationTarget();
$('ul.link-list a').bind( "click", highlightClickTarget );
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,114 @@
/**
*
* JQuery QuickSearch - Hook up a form field to hide non-matching elements.
* $Id: quicksearch.js 53 2009-01-07 02:52:03Z deveiant $
*
* Author: Michael Granger <mgranger@laika.com>
*
*/
jQuery.fn.quicksearch = function( target, searchElems, options ) {
// console.debug( "Quicksearch fn" );
var settings = {
delay: 250,
clearButton: false,
highlightMatches: false,
focusOnLoad: false,
noSearchResultsIndicator: null
};
if ( options ) $.extend( settings, options );
return jQuery(this).each( function() {
// console.debug( "Creating a new quicksearch on %o for %o", this, searchElems );
new jQuery.quicksearch( this, searchElems, settings );
});
};
jQuery.quicksearch = function( searchBox, searchElems, settings ) {
var timeout;
var boxdiv = $(searchBox).parents('div').eq(0);
function init() {
setupKeyEventHandlers();
focusOnLoad();
};
function setupKeyEventHandlers() {
// console.debug( "Hooking up the 'keypress' event to %o", searchBox );
$(searchBox).
unbind( 'keyup' ).
keyup( function(e) { return onSearchKey( e.keyCode ); });
$(searchBox).
unbind( 'keypress' ).
keypress( function(e) {
switch( e.which ) {
// Execute the search on Enter, Tab, or Newline
case 9:
case 13:
case 10:
clearTimeout( timeout );
e.preventDefault();
doQuickSearch();
break;
// Allow backspace
case 8:
return true;
break;
// Only allow valid search characters
default:
return validQSChar( e.charCode );
}
});
};
function focusOnLoad() {
if ( !settings.focusOnLoad ) return false;
$(searchBox).focus();
};
function onSearchKey ( code ) {
clearTimeout( timeout );
// console.debug( "...scheduling search." );
timeout = setTimeout( doQuickSearch, settings.delay );
};
function validQSChar( code ) {
var c = String.fromCharCode( code );
return (
(c == ':') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z')
);
};
function doQuickSearch() {
var searchText = searchBox.value;
var pat = new RegExp( searchText, "im" );
var shownCount = 0;
if ( settings.noSearchResultsIndicator ) {
$('#' + settings.noSearchResultsIndicator).hide();
}
// All elements start out hidden
$(searchElems).each( function(index) {
var str = $(this).text();
if ( pat.test(str) ) {
shownCount += 1;
$(this).fadeIn();
} else {
$(this).hide();
}
});
if ( shownCount == 0 && settings.noSearchResultsIndicator ) {
$('#' + settings.noSearchResultsIndicator).slideDown();
}
};
init();
};

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,701 @@
/*
* "Darkfish" Rdoc CSS
* $Id: rdoc.css 54 2009-01-27 01:09:48Z deveiant $
*
* Author: Michael Granger <ged@FaerieMUD.org>
*
*/
/* Base Green is: #6C8C22 */
*{ padding: 0; margin: 0; }
body {
background: #efefef;
font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
}
body.class, body.module, body.file {
margin-left: 40px;
}
body.file-popup {
font-size: 90%;
margin-left: 0;
}
h1 {
font-size: 300%;
text-shadow: rgba(135,145,135,0.65) 2px 2px 3px;
color: #6C8C22;
}
h2,h3,h4 { margin-top: 1.5em; }
:link,
:visited {
color: #6C8C22;
text-decoration: none;
}
:link:hover,
:visited:hover {
border-bottom: 1px dotted #6C8C22;
}
pre {
background: #ddd;
padding: 0.5em 0;
}
/* @group Generic Classes */
.initially-hidden {
display: none;
}
.quicksearch-field {
width: 98%;
background: #ddd;
border: 1px solid #aaa;
height: 1.5em;
-webkit-border-radius: 4px;
}
.quicksearch-field:focus {
background: #f1edba;
}
.missing-docs {
font-size: 120%;
background: white url(images/wrench_orange.png) no-repeat 4px center;
color: #ccc;
line-height: 2em;
border: 1px solid #d00;
opacity: 1;
padding-left: 20px;
text-indent: 24px;
letter-spacing: 3px;
font-weight: bold;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
.target-section {
border: 2px solid #dcce90;
border-left-width: 8px;
padding: 0 1em;
background: #fff3c2;
}
/* @end */
/* @group Index Page, Standalone file pages */
body.indexpage {
margin: 1em 3em;
}
body.indexpage p,
body.indexpage div,
body.file p {
margin: 1em 0;
}
.indexpage ul,
.file #documentation ul {
line-height: 160%;
list-style: none;
}
.indexpage ul :link,
.indexpage ul :visited,
.file #documentation ul :link,
.file #documentation ul :visited {
font-size: 16px;
}
.indexpage li,
.file #documentation li {
padding-left: 20px;
background: url(images/bullet_black.png) no-repeat left 4px;
}
.indexpage li.module {
background: url(images/package.png) no-repeat left 4px;
}
.indexpage li.class {
background: url(images/ruby.png) no-repeat left 4px;
}
.indexpage li.file {
background: url(images/page_white_text.png) no-repeat left 4px;
}
.file li p,
.indexpage li p {
margin: 0 0;
}
/* @end */
/* @group Top-Level Structure */
.class #metadata,
.file #metadata,
.module #metadata {
float: left;
width: 260px;
}
.class #documentation,
.file #documentation,
.module #documentation {
margin: 2em 1em 5em 300px;
min-width: 340px;
}
.file #metadata {
margin: 0.8em;
}
#validator-badges {
clear: both;
margin: 1em 1em 2em;
}
/* @end */
/* @group Metadata Section */
#metadata .section {
background-color: #dedede;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border: 1px solid #aaa;
margin: 0 8px 16px;
font-size: 90%;
overflow: hidden;
}
#metadata h3.section-header {
margin: 0;
padding: 2px 8px;
background: #ccc;
color: #666;
-moz-border-radius-topleft: 4px;
-moz-border-radius-topright: 4px;
-webkit-border-top-left-radius: 4px;
-webkit-border-top-right-radius: 4px;
border-bottom: 1px solid #aaa;
}
#metadata #home-section h3.section-header {
border-bottom: 0;
}
#metadata ul,
#metadata dl,
#metadata p {
padding: 8px;
list-style: none;
}
#file-metadata ul {
padding-left: 28px;
list-style-image: url(images/page_green.png);
}
dl.svninfo {
color: #666;
margin: 0;
}
dl.svninfo dt {
font-weight: bold;
}
ul.link-list li {
white-space: nowrap;
}
ul.link-list .type {
font-size: 8px;
text-transform: uppercase;
color: white;
background: #969696;
padding: 2px 4px;
-webkit-border-radius: 5px;
}
/* @end */
/* @group Project Metadata Section */
#project-metadata {
margin-top: 3em;
}
.file #project-metadata {
margin-top: 0em;
}
#project-metadata .section {
border: 1px solid #aaa;
}
#project-metadata h3.section-header {
border-bottom: 1px solid #aaa;
position: relative;
}
#project-metadata h3.section-header .search-toggle {
position: absolute;
right: 5px;
}
#project-metadata form {
color: #777;
background: #ccc;
padding: 8px 8px 16px;
border-bottom: 1px solid #bbb;
}
#project-metadata fieldset {
border: 0;
}
#no-class-search-results {
margin: 0 auto 1em;
text-align: center;
font-size: 14px;
font-weight: bold;
color: #aaa;
}
/* @end */
/* @group Documentation Section */
#description {
font-size: 100%;
color: #333;
}
#description p {
margin: 1em 0.4em;
}
#description ul {
margin-left: 2em;
}
#description ul li {
line-height: 1.4em;
}
#description dl,
#documentation dl {
margin: 8px 1.5em;
border: 1px solid #ccc;
}
#description dl {
font-size: 14px;
}
#description dt,
#documentation dt {
padding: 2px 4px;
font-weight: bold;
background: #ddd;
}
#description dd,
#documentation dd {
padding: 2px 12px;
}
#description dd + dt,
#documentation dd + dt {
margin-top: 0.7em;
}
#documentation .section {
font-size: 90%;
}
#documentation h3.section-header {
margin-top: 2em;
padding: 0.75em 0.5em;
background-color: #dedede;
color: #333;
font-size: 150%;
border: 1px solid #bbb;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
#constants-list > dl,
#attributes-list > dl {
margin: 1em 0 2em;
border: 0;
}
#constants-list > dl dt,
#attributes-list > dl dt {
padding-left: 0;
font-weight: bold;
font-family: Monaco, "Andale Mono";
background: inherit;
}
#constants-list > dl dt a,
#attributes-list > dl dt a {
color: inherit;
}
#constants-list > dl dd,
#attributes-list > dl dd {
margin: 0 0 1em 0;
padding: 0;
color: #666;
}
/* @group Method Details */
#documentation .method-source-code {
display: none;
}
#documentation .method-detail {
margin: 0.5em 0;
padding: 0.5em 0;
cursor: pointer;
}
#documentation .method-detail:hover {
background-color: #f1edba;
}
#documentation .method-heading {
position: relative;
padding: 2px 4px 0 20px;
font-size: 125%;
font-weight: bold;
color: #333;
background: url(images/brick.png) no-repeat left bottom;
}
#documentation .method-heading :link,
#documentation .method-heading :visited {
color: inherit;
}
#documentation .method-click-advice {
position: absolute;
top: 2px;
right: 5px;
font-size: 10px;
color: #9b9877;
visibility: hidden;
padding-right: 20px;
line-height: 20px;
background: url(images/zoom.png) no-repeat right top;
}
#documentation .method-detail:hover .method-click-advice {
visibility: visible;
}
#documentation .method-alias .method-heading {
color: #666;
background: url(images/brick_link.png) no-repeat left bottom;
}
#documentation .method-description,
#documentation .aliases {
margin: 0 20px;
line-height: 1.2em;
color: #666;
}
#documentation .aliases {
padding-top: 4px;
font-style: italic;
cursor: default;
}
#documentation .method-description p {
padding: 0;
}
#documentation .method-description p + p {
margin-bottom: 0.5em;
}
#documentation .attribute-method-heading {
background: url(images/tag_green.png) no-repeat left bottom;
}
#documentation #attribute-method-details .method-detail:hover {
background-color: transparent;
cursor: default;
}
#documentation .attribute-access-type {
font-size: 60%;
text-transform: uppercase;
vertical-align: super;
padding: 0 2px;
}
/* @end */
/* @end */
/* @group Source Code */
div.method-source-code {
background: #262626;
color: #efefef;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: hidden;
}
div.method-source-code pre {
background: inherit;
padding: 0;
color: white;
overflow: auto;
}
/* @group Ruby keyword styles */
.ruby-constant { color: #7fffd4; background: transparent; }
.ruby-keyword { color: #00ffff; background: transparent; }
.ruby-ivar { color: #eedd82; background: transparent; }
.ruby-operator { color: #00ffee; background: transparent; }
.ruby-identifier { color: #ffdead; background: transparent; }
.ruby-node { color: #ffa07a; background: transparent; }
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
.ruby-regexp { color: #ffa07a; background: transparent; }
.ruby-value { color: #7fffd4; background: transparent; }
/* @end */
/* @end */
/* @group File Popup Contents */
.file #metadata,
.file-popup #metadata {
}
.file-popup dl {
font-size: 80%;
padding: 0.75em;
background-color: #dedede;
color: #333;
border: 1px solid #bbb;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
.file dt {
font-weight: bold;
padding-left: 22px;
line-height: 20px;
background: url(images/page_white_width.png) no-repeat left top;
}
.file dt.modified-date {
background: url(images/date.png) no-repeat left top;
}
.file dt.requires {
background: url(images/plugin.png) no-repeat left top;
}
.file dt.scs-url {
background: url(images/wrench.png) no-repeat left top;
}
.file dl dd {
margin: 0 0 1em 0;
}
.file #metadata dl dd ul {
list-style: circle;
margin-left: 20px;
padding-top: 0;
}
.file #metadata dl dd ul li {
}
.file h2 {
margin-top: 2em;
padding: 0.75em 0.5em;
background-color: #dedede;
color: #333;
font-size: 120%;
border: 1px solid #bbb;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
/* @end */
/* @group ThickBox Styles */
#TB_window {
font: 12px Arial, Helvetica, sans-serif;
color: #333333;
}
#TB_secondLine {
font: 10px Arial, Helvetica, sans-serif;
color:#666666;
}
#TB_window :link,
#TB_window :visited { color: #666666; }
#TB_window :link:hover,
#TB_window :visited:hover { color: #000; }
#TB_window :link:active,
#TB_window :visited:active { color: #666666; }
#TB_window :link:focus,
#TB_window :visited:focus { color: #666666; }
#TB_overlay {
position: fixed;
z-index:100;
top: 0px;
left: 0px;
height:100%;
width:100%;
}
.TB_overlayMacFFBGHack {background: url(images/macFFBgHack.png) repeat;}
.TB_overlayBG {
background-color:#000;
filter:alpha(opacity=75);
-moz-opacity: 0.75;
opacity: 0.75;
}
* html #TB_overlay { /* ie6 hack */
position: absolute;
height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_window {
position: fixed;
background: #ffffff;
z-index: 102;
color:#000000;
display:none;
border: 4px solid #525252;
text-align:left;
top:50%;
left:50%;
}
* html #TB_window { /* ie6 hack */
position: absolute;
margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_window img#TB_Image {
display:block;
margin: 15px 0 0 15px;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-top: 1px solid #666;
border-left: 1px solid #666;
}
#TB_caption{
height:25px;
padding:7px 30px 10px 25px;
float:left;
}
#TB_closeWindow{
height:25px;
padding:11px 25px 10px 0;
float:right;
}
#TB_closeAjaxWindow{
padding:7px 10px 5px 0;
margin-bottom:1px;
text-align:right;
float:right;
}
#TB_ajaxWindowTitle{
float:left;
padding:7px 0 5px 10px;
margin-bottom:1px;
font-size: 22px;
}
#TB_title{
background-color: #6C8C22;
color: #dedede;
height:40px;
}
#TB_title :link,
#TB_title :visited {
color: white !important;
border-bottom: 1px dotted #dedede;
}
#TB_ajaxContent{
clear:both;
padding:2px 15px 15px 15px;
overflow:auto;
text-align:left;
line-height:1.4em;
}
#TB_ajaxContent.TB_modal{
padding:15px;
}
#TB_ajaxContent p{
padding:5px 0px 5px 0px;
}
#TB_load{
position: fixed;
display:none;
height:13px;
width:208px;
z-index:103;
top: 50%;
left: 50%;
margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
}
* html #TB_load { /* ie6 hack */
position: absolute;
margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_HideSelect{
z-index:99;
position:fixed;
top: 0;
left: 0;
background-color:#fff;
border:none;
filter:alpha(opacity=0);
-moz-opacity: 0;
opacity: 0;
height:100%;
width:100%;
}
* html #TB_HideSelect { /* ie6 hack */
position: absolute;
height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_iframeContent{
clear:both;
border:none;
margin-bottom:-1px;
margin-top:1px;
_margin-bottom:1px;
}
/* @end */
/* @group Debugging Section */
#debugging-toggle {
text-align: center;
}
#debugging-toggle img {
cursor: pointer;
}
#rdoc-debugging-section-dump {
display: none;
margin: 0 2em 2em;
background: #ccc;
border: 1px solid #999;
}
/* @end */

View file

@ -1,81 +0,0 @@
require 'rdoc/rdoc'
require 'rdoc/generator'
require 'rdoc/markup/to_texinfo'
module RDoc
module Generator
# This generates Texinfo files for viewing with GNU Info or Emacs
# from RDoc extracted from Ruby source files.
class TEXINFO
# What should the .info file be named by default?
DEFAULT_INFO_FILENAME = 'rdoc.info'
include Generator::MarkUp
# Accept some options
def initialize(options)
@options = options
@options.inline_source = true
@options.op_name ||= 'rdoc.texinfo'
@options.formatter = ::RDoc::Markup::ToTexInfo.new
end
# Generate the +texinfo+ files
def generate(toplevels)
@toplevels = toplevels
@files, @classes = ::RDoc::Generator::Context.build_indices(@toplevels,
@options)
(@files + @classes).each { |x| x.value_hash }
open(@options.op_name, 'w') do |f|
f.puts TexinfoTemplate.new('files' => @files,
'classes' => @classes,
'filename' => @options.op_name.gsub(/texinfo/, 'info'),
'title' => @options.title).render
end
# TODO: create info files and install?
end
class << self
# Factory? We don't need no stinkin' factory!
alias_method :for, :new
end
end
# Basically just a wrapper around ERB.
# Should probably use RDoc::TemplatePage instead
class TexinfoTemplate
BASE_DIR = ::File.expand_path(::File.dirname(__FILE__)) # have to calculate this when the file's loaded.
def initialize(values, file = 'texinfo.erb')
@v, @file = [values, file]
end
def template
::File.read(::File.join(BASE_DIR, 'texinfo', @file))
end
# Go!
def render
ERB.new(template).result binding
end
def href(location, text)
text # TODO: how does texinfo do hyperlinks?
end
def target(name, text)
text # TODO: how do hyperlink targets work?
end
# TODO: this is probably implemented elsewhere?
def method_prefix(section)
{ 'Class' => '.',
'Module' => '::',
'Instance' => '#',
}[section['category']]
end
end
end
end

View file

@ -1,44 +0,0 @@
@node <%= @v['class']['full_name'].gsub(/::/, '-') %>
@chapter <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %>
<% if @v['class']["parent"] and @v['class']['par_url'] %>
Inherits <%= href @v['class']["par_url"], @v['class']["parent"] %><% end %>
<%= @v['class']["description"] %>
<% if @v['class']["includes"] %>
Includes
<% @v['class']["includes"].each do |include| %>
* <%= href include["aref"], include["name"] %>
<% end # @v['class']["includes"] %>
<% end %>
<% if @v['class']["sections"] %>
<% @v['class']["sections"].each do |section| %>
<% if section["attributes"] %>
Attributes
<% section["attributes"].each do |attributes| %>
* <%= attributes["name"] %> <%= attributes["rw"] %> <%= attributes["a_desc"] %>
<% end # section["attributes"] %>
<% end %>
<% end %>
<% @v['class']["sections"].each do |section| %>
<% if section["method_list"] %>
Methods
@menu
<% section["method_list"].each_with_index do |method_list, i| %>
<%= i %>
<% (method_list["methods"] || []).each do |method| %>
* <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix method_list %><%= method['name'] %>::<% end %>
<% end %>
@end menu
<% section["method_list"].each do |method_list| %>
<% (method_list["methods"] || []).uniq.each do |method| %>
<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}),
'method.texinfo.erb').render %><% end %>
<% end %>
<% end # if section["method_list"] %>
<% end # @v['class']["sections"] %>
<% end %>

View file

@ -1,6 +0,0 @@
<% if false %>
<h2>File: <%= @v['file']["short_name"] %></h2>
Path: <%= @v['file']["full_path"] %>
<%= TexinfoTemplate.new(@v, 'content.texinfo.erb').render %>
<% end %>

View file

@ -1,6 +0,0 @@
@node <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
@section <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
<%= @v['method']["type"] %> <%= @v['method']["category"] %> method:
<%= target @v['method']["aref"], @v['method']['callseq'] ||
@v['method']["name"] + @v['method']["params"] %>
<%= @v['method']["m_desc"] %>

View file

@ -1,28 +0,0 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename <%= @v['filename'] %>
@settitle <%= @v['title'] %>
@c %**end of header
@contents @c TODO: whitespace is a mess... =\
@ifnottex
@node Top
@top <%= @v['title'] %>
@end ifnottex
<% if @f = @v['files'].detect { |f| f.name =~ /Readme/i } %>
<%= @f.values['description'] %><% end %>
@menu
<% @v['classes'].each do |klass| %>
* <%= klass.name.gsub(/::/, '-') %>::<% end %>
@c TODO: add files
@end menu
<% (@v['classes'] || []).each_with_index do |klass, i| %>
<%= TexinfoTemplate.new(@v.merge('class' => klass.values),
'class.texinfo.erb').render %><% end %>
@bye

View file

@ -1,117 +0,0 @@
require 'rdoc/generator/html'
##
# Generate XML output as one big file
class RDoc::Generator::XML < RDoc::Generator::HTML
##
# Standard generator factory
def self.for(options)
new(options)
end
def initialize(*args)
super
end
##
# Build the initial indices and output objects
# based on an array of TopLevel objects containing
# the extracted information.
def generate(info)
@info = info
@files = []
@classes = []
@hyperlinks = {}
build_indices
generate_xml
end
##
# Generate:
#
# * a list of File objects for each TopLevel object.
# * a list of Class objects for each first level
# class or module in the TopLevel objects
# * a complete list of all hyperlinkable terms (file,
# class, module, and method names)
def build_indices
@info.each do |toplevel|
@files << RDoc::Generator::File.new(toplevel, @options, RDoc::Generator::FILE_DIR)
end
RDoc::TopLevel.all_classes_and_modules.each do |cls|
build_class_list(cls, @files[0], RDoc::Generator::CLASS_DIR)
end
end
def build_class_list(from, html_file, class_dir)
@classes << RDoc::Generator::Class.new(from, html_file, class_dir, @options)
from.each_classmodule do |mod|
build_class_list(mod, html_file, class_dir)
end
end
##
# Generate all the HTML. For the one-file case, we generate
# all the information in to one big hash
def generate_xml
values = {
'charset' => @options.charset,
'files' => gen_into(@files),
'classes' => gen_into(@classes)
}
template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name
opfile = File.open(@options.op_name, "w")
else
opfile = $stdout
end
template.write_html_on(opfile, values)
end
def gen_into(list)
res = []
list.each do |item|
res << item.value_hash
end
res
end
def gen_file_index
gen_an_index(@files, 'Files')
end
def gen_class_index
gen_an_index(@classes, 'Classes')
end
def gen_method_index
gen_an_index(RDoc::Generator::HtmlMethod.all_methods, 'Methods')
end
def gen_an_index(collection, title)
res = []
collection.sort.each do |f|
if f.document_self
res << { "href" => f.path, "name" => f.index_name }
end
end
return {
"entries" => res,
'list_title' => title,
'index_url' => main_url,
}
end
end

View file

@ -1,113 +0,0 @@
require 'rdoc/generator/xml'
module RDoc::Generator::XML::RDF
CONTENTS_RDF = <<-EOF
<% if defined? classes and classes["description"] then %>
<description rd:parseType="Literal">
<%= classes["description"] %>
</description>
<% end %>
<% if defined? files and files["requires"] then %>
<% files["requires"].each do |requires| %>
<rd:required-file rd:name="<%= requires["name"] %>" />
<% end # files["requires"] %>
<% end %>
<% if defined? classes and classes["includes"] then %>
<IncludedModuleList>
<% classes["includes"].each do |includes| %>
<included-module rd:name="<%= includes["name"] %>" />
<% end # includes["includes"] %>
</IncludedModuleList>
<% end %>
<% if defined? classes and classes["sections"] then %>
<% classes["sections"].each do |sections| %>
<% if sections["attributes"] then %>
<% sections["attributes"].each do |attributes| %>
<contents>
<Attribute rd:name="<%= attributes["name"] %>">
<% if attributes["rw"] then %>
<attribute-rw><%= attributes["rw"] %></attribute-rw>
<% end %>
<description rdf:parseType="Literal"><%= attributes["a_desc"] %></description>
</Attribute>
</contents>
<% end # sections["attributes"] %>
<% end %>
<% if sections["method_list"] then %>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<% method_list["methods"].each do |methods| %>
<contents>
<Method rd:name="<%= methods["name"] %>" rd:visibility="<%= methods["type"] %>"
rd:category="<%= methods["category"] %>" rd:id="<%= methods["aref"] %>">
<parameters><%= methods["params"] %></parameters>
<% if methods["m_desc"] then %>
<description rdf:parseType="Literal">
<%= methods["m_desc"] %>
</description>
<% end %>
<% if methods["sourcecode"] then %>
<source-code-listing rdf:parseType="Literal">
<%= methods["sourcecode"] %>
</source-code-listing>
<% end %>
</Method>
</contents>
<% end # method_list["methods"] %>
<% end %>
<% end # sections["method_list"] %>
<% end %>
<!-- end method list -->
<% end # classes["sections"] %>
<% end %>
EOF
########################################################################
ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://pragprog.com/rdoc/rdoc.rdf#"
xmlns:rd="http://pragprog.com/rdoc/rdoc.rdf#">
<!-- RDoc -->
<% values["files"].each do |files| %>
<rd:File rd:name="<%= files["short_name"] %>" rd:id="<%= files["href"] %>">
<path><%= files["full_path"] %></path>
<dtm-modified><%= files["dtm_modified"] %></dtm-modified>
} + CONTENTS_RDF + %{
</rd:File>
<% end # values["files"] %>
<% values["classes"].each do |classes| %>
<<%= values["classmod"] %> rd:name="<%= classes["full_name"] %>" rd:id="<%= classes["full_name"] %>">
<classmod-info>
<% if classes["infiles"] then %>
<InFiles>
<% classes["infiles"].each do |infiles| %>
<infile>
<File rd:name="<%= infiles["full_path"] %>"
<% if infiles["full_path_url"] then %>
rdf:about="<%= infiles["full_path_url"] %>"
<% end %>
/>
</infile>
<% end # classes["infiles"] %>
</InFiles>
<% end %>
<% if classes["parent"] then %>
<superclass><%= href classes["par_url"], classes["parent"] %></superclass>
<% end %>
</classmod-info>
} + CONTENTS_RDF + %{
</<%= classes["classmod"] %>>
<% end # values["classes"] %>
<!-- /RDoc -->
</rdf:RDF>
}
end

View file

@ -1,123 +0,0 @@
require 'rdoc/generator/xml'
module RDoc::Generator::XML::XML
CONTENTS_XML = <<-EOF
<% if defined? classes and classes["description"] then %>
<description>
<%= classes["description"] %>
</description>
<% end %>
<contents>
<% if defined? files and files["requires"] then %>
<required-file-list>
<% files["requires"].each do |requires| %>
<required-file name="<%= requires["name"] %>"
<% if requires["aref"] then %>
href="<%= requires["aref"] %>"
<% end %>
/>
<% end %><%# files["requires"] %>
</required-file-list>
<% end %>
<% if defined? classes and classes["sections"] then %>
<% classes["sections"].each do |sections| %>
<% if sections["constants"] then %>
<constant-list>
<% sections["constants"].each do |constant| %>
<constant name="<%= constant["name"] %>">
<% if constant["value"] then %>
<value><%= constant["value"] %></value>
<% end %>
<description><%= constant["a_desc"] %></description>
</constant>
<% end %><%# sections["constants"] %>
</constant-list>
<% end %>
<% if sections["attributes"] then %>
<attribute-list>
<% sections["attributes"].each do |attributes| %>
<attribute name="<%= attributes["name"] %>">
<% if attributes["rw"] then %>
<attribute-rw><%= attributes["rw"] %></attribute-rw>
<% end %>
<description><%= attributes["a_desc"] %></description>
</attribute>
<% end %><%# sections["attributes"] %>
</attribute-list>
<% end %>
<% if sections["method_list"] then %>
<method-list>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<% method_list["methods"].each do |methods| %>
<method name="<%= methods["name"] %>" type="<%= methods["type"] %>" category="<%= methods["category"] %>" id="<%= methods["aref"] %>">
<parameters><%= methods["params"] %></parameters>
<% if methods["m_desc"] then %>
<description>
<%= methods["m_desc"] %>
</description>
<% end %>
<% if methods["sourcecode"] then %>
<source-code-listing>
<%= methods["sourcecode"] %>
</source-code-listing>
<% end %>
</method>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
</method-list>
<% end %>
<% end %><%# classes["sections"] %>
<% end %>
<% if defined? classes and classes["includes"] then %>
<included-module-list>
<% classes["includes"].each do |includes| %>
<included-module name="<%= includes["name"] %>"
<% if includes["aref"] then %>
href="<%= includes["aref"] %>"
<% end %>
/>
<% end %><%# classes["includes"] %>
</included-module-list>
<% end %>
</contents>
EOF
ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
<rdoc>
<file-list>
<% values["files"].each do |files| %>
<file name="<%= files["short_name"] %>" id="<%= files["href"] %>">
<file-info>
<path><%= files["full_path"] %></path>
<dtm-modified><%= files["dtm_modified"] %></dtm-modified>
</file-info>
} + CONTENTS_XML + %{
</file>
<% end %><%# values["files"] %>
</file-list>
<class-module-list>
<% values["classes"].each do |classes| %>
<<%= classes["classmod"] %> name="<%= classes["full_name"] %>" id="<%= classes["full_name"] %>">
<classmod-info>
<% if classes["infiles"] then %>
<infiles>
<% classes["infiles"].each do |infiles| %>
<infile><%= href infiles["full_path_url"], infiles["full_path"] %></infile>
<% end %><%# classes["infiles"] %>
</infiles>
<% end %>
<% if classes["parent"] then %>
<superclass><%= href classes["par_url"], classes["parent"] %></superclass>
<% end %>
</classmod-info>
} + CONTENTS_XML + %{
</<%= classes["classmod"] %>>
<% end %><%# values["classes"] %>
</class-module-list>
</rdoc>
}
end

8
lib/rdoc/ghost_method.rb Normal file
View file

@ -0,0 +1,8 @@
require 'rdoc/any_method'
##
# GhostMethod represents a method referenced only by a comment
class RDoc::GhostMethod < RDoc::AnyMethod
end

61
lib/rdoc/include.rb Normal file
View file

@ -0,0 +1,61 @@
require 'rdoc/code_object'
##
# A Module include in a class with \#include
class RDoc::Include < RDoc::CodeObject
##
# Name of included module
attr_accessor :name
##
# Creates a new Include for +name+ with +comment+
def initialize(name, comment)
super()
@name = name
self.comment = comment
end
##
# Includes are sorted by name
def <=> other
return unless self.class === other
name <=> other.name
end
def == other # :nodoc:
self.class == other.class and
self.name == other.name
end
##
# Full name based on #module
def full_name
m = self.module
RDoc::ClassModule === m ? m.full_name : @name
end
def inspect # :nodoc:
"#<%s:0x%x %s.include %s>" % [
self.class,
object_id,
parent_name, @name,
]
end
##
# Attempts to locate the included module object. Returns the name if not
# known.
def module
RDoc::TopLevel.find_module_named(@name) || @name
end
end

View file

@ -27,9 +27,9 @@ require 'rdoc'
# convert multiple input strings.
#
# require 'rdoc/markup/to_html'
#
#
# h = RDoc::Markup::ToHtml.new
#
#
# puts h.convert(input_string)
#
# You can extend the RDoc::Markup parser to recognise new markup
@ -41,22 +41,22 @@ require 'rdoc'
#
# require 'rdoc/markup'
# require 'rdoc/markup/to_html'
#
#
# class WikiHtml < RDoc::Markup::ToHtml
# def handle_special_WIKIWORD(special)
# "<font color=red>" + special.text + "</font>"
# end
# end
#
#
# m = RDoc::Markup.new
# m.add_word_pair("{", "}", :STRIKE)
# m.add_html("no", :STRIKE)
#
#
# m.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
#
#
# wh = WikiHtml.new
# wh.add_tag(:STRIKE, "<strike>", "</strike>")
#
#
# puts "<body>#{wh.convert ARGF.read}</body>"
#
#--
@ -65,30 +65,7 @@ require 'rdoc'
class RDoc::Markup
SPACE = ?\s
# List entries look like:
# * text
# 1. text
# [label] text
# label:: text
#
# Flag it as a list entry, and work out the indent for subsequent lines
SIMPLE_LIST_RE = /^(
( \* (?# bullet)
|- (?# bullet)
|\d+\. (?# numbered )
|[A-Za-z]\. (?# alphabetically numbered )
)
\s+
)\S/x
LABEL_LIST_RE = /^(
( \[.*?\] (?# labeled )
|\S.*:: (?# note )
)(?:\s+|$)
)/x
attr_reader :attribute_manager
##
# Take a block of text and use various heuristics to determine it's
@ -96,7 +73,7 @@ class RDoc::Markup
# identify significant chunks.
def initialize
@am = RDoc::Markup::AttributeManager.new
@attribute_manager = RDoc::Markup::AttributeManager.new
@output = nil
end
@ -106,14 +83,14 @@ class RDoc::Markup
# formatters can recognize by their +name+.
def add_word_pair(start, stop, name)
@am.add_word_pair(start, stop, name)
@attribute_manager.add_word_pair(start, stop, name)
end
##
# Add to the sequences recognized as general markup.
def add_html(tag, name)
@am.add_html(tag, name)
@attribute_manager.add_html(tag, name)
end
##
@ -126,7 +103,7 @@ class RDoc::Markup
# accept_special method.
def add_special(pattern, name)
@am.add_special(pattern, name)
@attribute_manager.add_special(pattern, name)
end
##
@ -136,243 +113,14 @@ class RDoc::Markup
# display the result.
def convert(str, op)
lines = str.split(/\r?\n/).map { |line| Line.new line }
@lines = Lines.new lines
document = RDoc::Markup::Parser.parse str
return "" if @lines.empty?
@lines.normalize
assign_types_to_lines
group = group_lines
# call the output formatter to handle the result
#group.each { |line| p line }
group.accept @am, op
document.accept op
end
private
##
# Look through the text at line indentation. We flag each line as being
# Blank, a paragraph, a list element, or verbatim text.
def assign_types_to_lines(margin = 0, level = 0)
while line = @lines.next
if line.blank? then
line.stamp :BLANK, level
next
end
# if a line contains non-blanks before the margin, then it must belong
# to an outer level
text = line.text
for i in 0...margin
if text[i] != SPACE
@lines.unget
return
end
end
active_line = text[margin..-1]
# Rules (horizontal lines) look like
#
# --- (three or more hyphens)
#
# The more hyphens, the thicker the rule
#
if /^(---+)\s*$/ =~ active_line
line.stamp :RULE, level, $1.length-2
next
end
# Then look for list entries. First the ones that have to have
# text following them (* xxx, - xxx, and dd. xxx)
if SIMPLE_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
prefix_length = prefix.length
flag = case prefix
when "*","-" then :BULLET
when /^\d/ then :NUMBER
when /^[A-Z]/ then :UPPERALPHA
when /^[a-z]/ then :LOWERALPHA
else raise "Invalid List Type: #{self.inspect}"
end
line.stamp :LIST, level+1, prefix, flag
text[margin, prefix_length] = " " * prefix_length
assign_types_to_lines(offset, level + 1)
next
end
if LABEL_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
prefix_length = prefix.length
next if handled_labeled_list(line, level, margin, offset, prefix)
end
# Headings look like
# = Main heading
# == Second level
# === Third
#
# Headings reset the level to 0
if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
prefix_length = $1.length
prefix_length = 6 if prefix_length > 6
line.stamp :HEADING, 0, prefix_length
line.strip_leading(margin + prefix_length)
next
end
# If the character's a space, then we have verbatim text,
# otherwise
if active_line[0] == SPACE
line.strip_leading(margin) if margin > 0
line.stamp :VERBATIM, level
else
line.stamp :PARAGRAPH, level
end
end
end
##
# Handle labeled list entries, We have a special case to deal with.
# Because the labels can be long, they force the remaining block of text
# over the to right:
#
# this is a long label that I wrote:: and here is the
# block of text with
# a silly margin
#
# So we allow the special case. If the label is followed by nothing, and
# if the following line is indented, then we take the indent of that line
# as the new margin.
#
# this is a long label that I wrote::
# here is a more reasonably indented block which
# will be attached to the label.
#
def handled_labeled_list(line, level, margin, offset, prefix)
prefix_length = prefix.length
text = line.text
flag = nil
case prefix
when /^\[/ then
flag = :LABELED
prefix = prefix[1, prefix.length-2]
when /:$/ then
flag = :NOTE
prefix.chop!
else
raise "Invalid List Type: #{self.inspect}"
end
# body is on the next line
if text.length <= offset then
original_line = line
line = @lines.next
return false unless line
text = line.text
for i in 0..margin
if text[i] != SPACE
@lines.unget
return false
end
end
i = margin
i += 1 while text[i] == SPACE
if i >= text.length then
@lines.unget
return false
else
offset = i
prefix_length = 0
if text[offset..-1] =~ SIMPLE_LIST_RE then
@lines.unget
line = original_line
line.text = ''
else
@lines.delete original_line
end
end
end
line.stamp :LIST, level+1, prefix, flag
text[margin, prefix_length] = " " * prefix_length
assign_types_to_lines(offset, level + 1)
return true
end
##
# Return a block consisting of fragments which are paragraphs, list
# entries or verbatim text. We merge consecutive lines of the same type
# and level together. We are also slightly tricky with lists: the lines
# following a list introduction look like paragraph lines at the next
# level, and we remap them into list entries instead.
def group_lines
@lines.rewind
in_list = false
wanted_type = wanted_level = nil
block = LineCollection.new
group = nil
while line = @lines.next
if line.level == wanted_level and line.type == wanted_type
group.add_text(line.text)
else
group = block.fragment_for(line)
block.add(group)
if line.type == :LIST
wanted_type = :PARAGRAPH
else
wanted_type = line.type
end
wanted_level = line.type == :HEADING ? line.param : line.level
end
end
block.normalize
block
end
##
# For debugging, we allow access to our line contents as text.
def content
@lines.as_text
end
public :content
##
# For debugging, return the list of line types.
def get_line_types
@lines.line_types
end
public :get_line_types
end
require 'rdoc/markup/fragments'
require 'rdoc/markup/parser'
require 'rdoc/markup/attribute_manager'
require 'rdoc/markup/inline'
require 'rdoc/markup/lines'

View file

@ -1,41 +1,76 @@
require 'rdoc/markup/inline'
##
# 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
PROTECT_ATTR = A_PROTECT.chr
A_PROTECT = 004 # :nodoc:
PROTECT_ATTR = A_PROTECT.chr # :nodoc:
##
# 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 = {}
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
WORD_PAIR_MAP = {}
attr_reader :word_pair_map
##
# This maps HTML tags to the corresponding attribute char
HTML_TAGS = {}
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
SPECIAL = {}
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 = {}
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
@ -75,19 +110,19 @@ class RDoc::Markup::AttributeManager
def convert_attrs(str, attrs)
# first do matching ones
tags = MATCHING_WORD_PAIRS.keys.join("")
tags = @matching_word_pairs.keys.join("")
re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
1 while str.gsub!(re) do
attr = MATCHING_WORD_PAIRS[$2]
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|
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
@ -96,11 +131,14 @@ class RDoc::Markup::AttributeManager
end
end
##
# Converts HTML tags to RDoc attributes
def convert_html(str, attrs)
tags = HTML_TAGS.keys.join '|'
tags = @html_tags.keys.join '|'
1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
attr = HTML_TAGS[$1.downcase]
attr = @html_tags[$1.downcase]
html_length = $1.length + 2
seq = NULL * html_length
attrs.set_attrs($`.length + html_length, $2.length, attr)
@ -108,9 +146,12 @@ class RDoc::Markup::AttributeManager
}
end
##
# Converts special sequences to RDoc attributes
def convert_specials(str, attrs)
unless SPECIAL.empty?
SPECIAL.each do |regexp, attr|
unless @special.empty?
@special.each do |regexp, attr|
str.scan(regexp) do
attrs.set_attrs($`.length, $&.length,
attr | RDoc::Markup::Attribute::SPECIAL)
@ -120,31 +161,25 @@ class RDoc::Markup::AttributeManager
end
##
# A \ in front of a character that would normally be processed turns off
# processing. We do this by turning \< into <#{PROTECT}
PROTECTABLE = %w[<\\]
# Escapes special sequences of text to prevent conversion to RDoc
def mask_protected_sequences
protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
@str.gsub!(protect_pattern, "\\1#{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
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)
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
@ -153,24 +188,39 @@ class RDoc::Markup::AttributeManager
bitmap = RDoc::Markup::Attribute.bitmap_for name
if start == stop then
MATCHING_WORD_PAIRS[start] = bitmap
@matching_word_pairs[start] = bitmap
else
pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/
WORD_PAIR_MAP[pattern] = bitmap
@word_pair_map[pattern] = bitmap
end
PROTECTABLE << start[0,1]
PROTECTABLE.uniq!
@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] = RDoc::Markup::Attribute.bitmap_for name
@html_tags[tag.downcase] = RDoc::Markup::Attribute.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] = RDoc::Markup::Attribute.bitmap_for name
@special[pattern] = RDoc::Markup::Attribute.bitmap_for name
end
##
# Processes +str+ converting attributes, HTML and specials
def flow(str)
@str = str
@ -178,15 +228,18 @@ class RDoc::Markup::AttributeManager
@attrs = RDoc::Markup::AttrSpan.new @str.length
convert_attrs(@str, @attrs)
convert_html(@str, @attrs)
convert_specials(str, @attrs)
convert_attrs @str, @attrs
convert_html @str, @attrs
convert_specials @str, @attrs
unmask_protected_sequences
return split_into_flow
split_into_flow
end
##
# Debug method that prints a string along with its attributes
def display_attributes
puts
puts @str.tr(NULL, "!")
@ -258,7 +311,7 @@ class RDoc::Markup::AttributeManager
# and reset to all attributes off
res << change_attribute(current_attr, 0) if current_attr != 0
return res
res
end
end

View file

@ -0,0 +1,19 @@
##
# An empty line
class RDoc::Markup::BlankLine
def == other # :nodoc:
self.class == other.class
end
def accept visitor
visitor.accept_blank_line self
end
def pretty_print q # :nodoc:
q.text 'blankline'
end
end

View file

@ -0,0 +1,72 @@
##
# A Document containing lists, headings, paragraphs, etc.
class RDoc::Markup::Document
##
# The parts of the Document
attr_reader :parts
##
# Creates a new Document with +parts+
def initialize *parts
@parts = []
@parts.push(*parts)
end
##
# Appends +part+ to the document
def << part
case part
when RDoc::Markup::Document then
unless part.empty? then
parts.push(*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 @parts == other.parts
end
def accept visitor
visitor.start_accepting
@parts.each do |item|
item.accept visitor
end
visitor.end_accepting
end
def empty?
@parts.empty?
end
def pretty_print q # :nodoc:
q.group 2, '[doc: ', ']' do
q.seplist @parts do |part|
q.pp part
end
end
end
##
# Appends +parts+ to the document
def push *parts
self.parts.push(*parts)
end
end

View file

@ -1,14 +1,143 @@
require 'rdoc/markup'
##
# Base class for RDoc markup formatters
#
# Formatters use a visitor pattern to convert content into output.
class RDoc::Markup::Formatter
InlineTag = Struct.new(:bit, :on, :off)
##
# Creates a new Formatter
def initialize
@markup = RDoc::Markup.new
@am = @markup.attribute_manager
@attr_tags = []
@in_tt = 0
@tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
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 = RDoc::Markup::Attribute.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)
handled = false
RDoc::Markup::Attribute.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
raise "Unhandled special: #{special}" unless handled
special.text
end
##
# Converts a string to be fancier if desired
def convert_string string
string
end
##
# Are we currently inside tt tags?
def in_tt?
@in_tt > 0
end
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
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
##
# Is +tag+ a tt tag?
def tt? tag
tag.bit == @tt_bit
end
end
class RDoc::Markup
autoload :ToAnsi, 'rdoc/markup/to_ansi'
autoload :ToBs, 'rdoc/markup/to_bs'
autoload :ToHtml, 'rdoc/markup/to_html'
autoload :ToHtmlCrossref, 'rdoc/markup/to_html_crossref'
autoload :ToRdoc, 'rdoc/markup/to_rdoc'
end

View file

@ -0,0 +1,341 @@
require 'minitest/unit'
require 'rdoc/markup/formatter'
##
# Test case for creating new RDoc::Markup formatters. See
# test/test_rdoc_markup_to_*.rb for examples.
class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
def setup
super
@m = RDoc::Markup.new
@am = RDoc::Markup::AttributeManager.new
@RM = RDoc::Markup
@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
def self.add_visitor_tests
self.class_eval do
def test_start_accepting
@to.start_accepting
start_accepting
end
def test_end_accepting
@to.start_accepting
@to.res << 'hi'
end_accepting
end
def test_accept_blank_line
@to.start_accepting
@to.accept_blank_line @RM::BlankLine.new
accept_blank_line
end
def test_accept_heading
@to.start_accepting
@to.accept_heading @RM::Heading.new(5, 'Hello')
accept_heading
end
def test_accept_paragraph
@to.start_accepting
@to.accept_paragraph @RM::Paragraph.new('hi')
accept_paragraph
end
def test_accept_verbatim
@to.start_accepting
@to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
' ', 'world', "\n")
accept_verbatim
end
def test_accept_rule
@to.start_accepting
@to.accept_rule @RM::Rule.new(4)
accept_rule
end
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
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
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
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
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
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
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
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
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
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
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
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
def test_accept_list_start_bullet
@to.start_accepting
@to.accept_list_start @bullet_list
accept_list_start_bullet
end
def test_accept_list_start_label
@to.start_accepting
@to.accept_list_start @label_list
accept_list_start_label
end
def test_accept_list_start_lalpha
@to.start_accepting
@to.accept_list_start @lalpha_list
accept_list_start_lalpha
end
def test_accept_list_start_note
@to.start_accepting
@to.accept_list_start @note_list
accept_list_start_note
end
def test_accept_list_start_number
@to.start_accepting
@to.accept_list_start @number_list
accept_list_start_number
end
def test_accept_list_start_ualpha
@to.start_accepting
@to.accept_list_start @ualpha_list
accept_list_start_ualpha
end
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
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
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
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
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
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
end
end
end

View file

@ -1,337 +0,0 @@
require 'rdoc/markup'
require 'rdoc/markup/lines'
class RDoc::Markup
##
# A Fragment is a chunk of text, subclassed as a paragraph, a list
# entry, or verbatim text.
class Fragment
attr_reader :level, :param, :txt
attr_accessor :type
##
# This is a simple factory system that lets us associate fragement
# types (a string) with a subclass of fragment
TYPE_MAP = {}
def self.type_name(name)
TYPE_MAP[name] = self
end
def self.for(line)
klass = TYPE_MAP[line.type] ||
raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
return klass.new(line.level, line.param, line.flag, line.text)
end
def initialize(level, param, type, txt)
@level = level
@param = param
@type = type
@txt = ""
add_text(txt) if txt
end
def add_text(txt)
@txt << " " if @txt.length > 0
@txt << txt.tr_s("\n ", " ").strip
end
def to_s
"L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
end
end
##
# A paragraph is a fragment which gets wrapped to fit. We remove all
# newlines when we're created, and have them put back on output.
class Paragraph < Fragment
type_name :PARAGRAPH
end
class BlankLine < Paragraph
type_name :BLANK
end
class Heading < Paragraph
type_name :HEADING
def head_level
@param.to_i
end
end
##
# A List is a fragment with some kind of label
class ListBase < Paragraph
LIST_TYPES = [
:BULLET,
:NUMBER,
:UPPERALPHA,
:LOWERALPHA,
:LABELED,
:NOTE,
]
end
class ListItem < ListBase
type_name :LIST
def to_s
text = if [:NOTE, :LABELED].include? type then
"#{@param}: #{@txt}"
else
@txt
end
"L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}"
end
end
class ListStart < ListBase
def initialize(level, param, type)
super(level, param, type, nil)
end
end
class ListEnd < ListBase
def initialize(level, type)
super(level, "", type, nil)
end
end
##
# Verbatim code contains lines that don't get wrapped.
class Verbatim < Fragment
type_name :VERBATIM
def add_text(txt)
@txt << txt.chomp << "\n"
end
end
##
# A horizontal rule
class Rule < Fragment
type_name :RULE
end
##
# Collect groups of lines together. Each group will end up containing a flow
# of text.
class LineCollection
def initialize
@fragments = []
end
def add(fragment)
@fragments << fragment
end
def each(&b)
@fragments.each(&b)
end
def to_a # :nodoc:
@fragments.map {|fragment| fragment.to_s}
end
##
# Factory for different fragment types
def fragment_for(*args)
Fragment.for(*args)
end
##
# Tidy up at the end
def normalize
change_verbatim_blank_lines
add_list_start_and_ends
add_list_breaks
tidy_blank_lines
end
def to_s
@fragments.join("\n----\n")
end
def accept(am, visitor)
visitor.start_accepting
@fragments.each do |fragment|
case fragment
when Verbatim
visitor.accept_verbatim(am, fragment)
when Rule
visitor.accept_rule(am, fragment)
when ListStart
visitor.accept_list_start(am, fragment)
when ListEnd
visitor.accept_list_end(am, fragment)
when ListItem
visitor.accept_list_item(am, fragment)
when BlankLine
visitor.accept_blank_line(am, fragment)
when Heading
visitor.accept_heading(am, fragment)
when Paragraph
visitor.accept_paragraph(am, fragment)
end
end
visitor.end_accepting
end
private
# If you have:
#
# normal paragraph text.
#
# this is code
#
# and more code
#
# You'll end up with the fragments Paragraph, BlankLine, Verbatim,
# BlankLine, Verbatim, BlankLine, etc.
#
# The BlankLine in the middle of the verbatim chunk needs to be changed to
# a real verbatim newline, and the two verbatim blocks merged
def change_verbatim_blank_lines
frag_block = nil
blank_count = 0
@fragments.each_with_index do |frag, i|
if frag_block.nil?
frag_block = frag if Verbatim === frag
else
case frag
when Verbatim
blank_count.times { frag_block.add_text("\n") }
blank_count = 0
frag_block.add_text(frag.txt)
@fragments[i] = nil # remove out current fragment
when BlankLine
if frag_block
blank_count += 1
@fragments[i] = nil
end
else
frag_block = nil
blank_count = 0
end
end
end
@fragments.compact!
end
##
# List nesting is implicit given the level of indentation. Make it
# explicit, just to make life a tad easier for the output processors
def add_list_start_and_ends
level = 0
res = []
type_stack = []
@fragments.each do |fragment|
# $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
new_level = fragment.level
while (level < new_level)
level += 1
type = fragment.type
res << ListStart.new(level, fragment.param, type) if type
type_stack.push type
# $stderr.puts "Start: #{level}"
end
while level > new_level
type = type_stack.pop
res << ListEnd.new(level, type) if type
level -= 1
# $stderr.puts "End: #{level}, #{type}"
end
res << fragment
level = fragment.level
end
level.downto(1) do |i|
type = type_stack.pop
res << ListEnd.new(i, type) if type
end
@fragments = res
end
##
# Inserts start/ends between list entries at the same level that have
# different element types
def add_list_breaks
res = @fragments
@fragments = []
list_stack = []
res.each do |fragment|
case fragment
when ListStart
list_stack.push fragment
when ListEnd
start = list_stack.pop
fragment.type = start.type
when ListItem
l = list_stack.last
if fragment.type != l.type
@fragments << ListEnd.new(l.level, l.type)
start = ListStart.new(l.level, fragment.param, fragment.type)
@fragments << start
list_stack.pop
list_stack.push start
end
else
;
end
@fragments << fragment
end
end
##
# Tidy up the blank lines:
# * change Blank/ListEnd into ListEnd/Blank
# * remove blank lines at the front
def tidy_blank_lines
(@fragments.size - 1).times do |i|
if BlankLine === @fragments[i] and ListEnd === @fragments[i+1] then
@fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
end
end
# remove leading blanks
@fragments.each_with_index do |f, i|
break unless f.kind_of? BlankLine
@fragments[i] = nil
end
@fragments.compact!
end
end
end

View file

@ -0,0 +1,17 @@
##
# A heading with a level (1-6) and text
class RDoc::Markup::Heading < Struct.new :level, :text
def accept visitor
visitor.accept_heading self
end
def pretty_print q # :nodoc:
q.group 2, "[head: #{level} ", ']' do
q.pp text
end
end
end

View file

@ -1,5 +1,3 @@
require 'rdoc/markup'
class RDoc::Markup
##
@ -7,6 +5,10 @@ class RDoc::Markup
# value.
class Attribute
##
# Special attribute type. See RDoc::Markup#add_special
SPECIAL = 1
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
@ -37,17 +39,18 @@ class RDoc::Markup
yield name.to_s if (bitmap & bit) != 0
end
end
end
AttrChanger = Struct.new(:turn_on, :turn_off)
AttrChanger = Struct.new :turn_on, :turn_off # :nodoc:
##
# 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 AttrChanger
def to_s
"Attr: +#{Attribute.as_string(turn_on)}/-#{Attribute.as_string(turn_on)}"
def to_s # :nodoc:
"Attr: +#{Attribute.as_string turn_on}/-#{Attribute.as_string turn_on}"
end
end
@ -55,42 +58,66 @@ class RDoc::Markup
# An array of attributes which parallels the characters in a string.
class 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
##
# Acccesses flags for character +n+
def [](n)
@attrs[n]
end
end
##
# Hold details of a special sequence
class 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
def inspect # :nodoc:
"#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
end
def to_s
def to_s # :nodoc:
"Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
end
@ -98,4 +125,3 @@ class RDoc::Markup
end
require 'rdoc/markup/attribute_manager'

View file

@ -1,152 +0,0 @@
class RDoc::Markup
##
# We store the lines we're working on as objects of class Line. These
# contain the text of the line, along with a flag indicating the line type,
# and an indentation level.
class Line
INFINITY = 9999
LINE_TYPES = [
:BLANK,
:HEADING,
:LIST,
:PARAGRAPH,
:RULE,
:VERBATIM,
]
# line type
attr_accessor :type
# The indentation nesting level
attr_accessor :level
# The contents
attr_accessor :text
# A prefix or parameter. For LIST lines, this is
# the text that introduced the list item (the label)
attr_accessor :param
# A flag. For list lines, this is the type of the list
attr_accessor :flag
# the number of leading spaces
attr_accessor :leading_spaces
# true if this line has been deleted from the list of lines
attr_accessor :deleted
def initialize(text)
@text = text.dup
@deleted = false
# expand tabs
1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
# Strip trailing whitespace
@text.sub!(/\s+$/, '')
# and look for leading whitespace
if @text.length > 0
@text =~ /^(\s*)/
@leading_spaces = $1.length
else
@leading_spaces = INFINITY
end
end
# Return true if this line is blank
def blank?
@text.empty?
end
# stamp a line with a type, a level, a prefix, and a flag
def stamp(type, level, param="", flag=nil)
@type, @level, @param, @flag = type, level, param, flag
end
##
# Strip off the leading margin
def strip_leading(size)
if @text.size > size
@text[0,size] = ""
else
@text = ""
end
end
def to_s
"#@type#@level: #@text"
end
end
##
# A container for all the lines.
class Lines
include Enumerable
attr_reader :lines # :nodoc:
def initialize(lines)
@lines = lines
rewind
end
def empty?
@lines.size.zero?
end
def each
@lines.each do |line|
yield line unless line.deleted
end
end
# def [](index)
# @lines[index]
# end
def rewind
@nextline = 0
end
def next
begin
res = @lines[@nextline]
@nextline += 1 if @nextline < @lines.size
end while res and res.deleted and @nextline < @lines.size
res
end
def unget
@nextline -= 1
end
def delete(a_line)
a_line.deleted = true
end
def normalize
margin = @lines.collect{|l| l.leading_spaces}.min
margin = 0 if margin == :INFINITY
@lines.each {|line| line.strip_leading(margin) } if margin > 0
end
def as_text
@lines.map {|l| l.text}.join("\n")
end
def line_types
@lines.map {|l| l.type }
end
end
end

78
lib/rdoc/markup/list.rb Normal file
View file

@ -0,0 +1,78 @@
##
# A List of ListItems
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+
def initialize type = nil, *items
@type = type
@items = []
@items.push(*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
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.push(*items)
end
end

View file

@ -0,0 +1,83 @@
##
# An item within a List that contains paragraphs, headings, etc.
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.push(*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
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
if @label then
q.text @label
q.breakable
end
q.seplist @parts do |part|
q.pp part
end
end
end
##
# Adds +parts+ to the ListItem
def push *parts
@parts.push(*parts)
end
end

View file

@ -0,0 +1,66 @@
##
# A Paragraph of text
class RDoc::Markup::Paragraph
##
# The component parts of the list
attr_reader :parts
##
# Creates a new Paragraph containing +parts+
def initialize *parts
@parts = []
@parts.push(*parts)
end
##
# Appends +text+ to the Paragraph
def << text
@parts << text
end
def == other # :nodoc:
self.class == other.class and text == other.text
end
def accept visitor
visitor.accept_paragraph self
end
##
# Appends +other+'s parts into this Paragraph
def merge other
@parts.push(*other.parts)
end
def pretty_print q # :nodoc:
self.class.name =~ /.*::(\w{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.push(*texts)
end
##
# The text of this paragraph
def text
@parts.join ' '
end
end

528
lib/rdoc/markup/parser.rb Normal file
View file

@ -0,0 +1,528 @@
require 'strscan'
require 'rdoc/text'
##
# 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
# <tt>\+blah\+</tt> 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
##
# Parsers +str+ into a Document
def self.parse str
parser = new
#parser.debug = true
parser.tokenize str
RDoc::Markup::Document.new(*parser.parse)
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
@tokens = []
@current_token = nil
@debug = false
@line = 0
@line_pos = 0
end
##
# Builds a Heading of +level+
def build_heading level
heading = RDoc::Markup::Heading.new level, text
skip :NEWLINE
heading
end
##
# Builds a List flush to +margin+
def build_list margin
p :list_start => margin if @debug
list = RDoc::Markup::List.new
until @tokens.empty? do
type, data, column, = get
case type
when :BULLET, :LABEL, :LALPHA, :NOTE, :NUMBER, :UALPHA then
list_type = type
if column < margin then
unget
break
end
if list.type and list.type != list_type then
unget
break
end
list.type = list_type
case type
when :NOTE, :LABEL then
_, indent, = get # SPACE
if :NEWLINE == peek_token.first then
get
peek_type, new_indent, peek_column, = peek_token
indent = new_indent if
peek_type == :INDENT and peek_column >= column
unget
end
else
data = nil
_, indent, = get
end
list_item = build_list_item(margin + indent, data)
list << list_item if list_item
else
unget
break
end
end
p :list_end => margin if @debug
return nil if list.empty?
list
end
##
# Builds a ListItem that is flush to +indent+ with type +item_type+
def build_list_item indent, item_type = nil
p :list_item_start => [indent, item_type] if @debug
list_item = RDoc::Markup::ListItem.new item_type
until @tokens.empty? do
type, data, column = get
if column < indent and
not type == :NEWLINE and
(type != :INDENT or data < indent) then
unget
break
end
case type
when :INDENT then
unget
list_item.push(*parse(indent))
when :TEXT then
unget
list_item << build_paragraph(indent)
when :HEADER then
list_item << build_heading(data)
when :NEWLINE then
list_item << RDoc::Markup::BlankLine.new
when *LIST_TOKENS then
unget
list_item << build_list(column)
else
raise ParseError, "Unhandled token #{@current_token.inspect}"
end
end
p :list_item_end => [indent, item_type] if @debug
return nil if list_item.empty?
list_item.parts.shift if
RDoc::Markup::BlankLine === list_item.parts.first and
list_item.length > 1
list_item
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
case type
when :INDENT then
next if data == margin and peek_token[0] == :TEXT
unget
break
when :TEXT then
if column != margin then
unget
break
end
paragraph << data
skip :NEWLINE
else
unget
break
end
end
p :paragraph_end => margin if @debug
paragraph
end
##
# Builds a Verbatim that is flush to +margin+
def build_verbatim margin
p :verbatim_begin => margin if @debug
verbatim = RDoc::Markup::Verbatim.new
until @tokens.empty? do
type, data, column, = get
case type
when :INDENT then
if margin >= data then
unget
break
end
indent = data - margin
verbatim << ' ' * indent
when :HEADER then
verbatim << '=' * data
_, _, peek_column, = peek_token
peek_column ||= column + data
verbatim << ' ' * (peek_column - column - data)
when :RULE then
width = 2 + data
verbatim << '-' * width
_, _, peek_column, = peek_token
peek_column ||= column + data + 2
verbatim << ' ' * (peek_column - column - width)
when :TEXT then
verbatim << data
when *LIST_TOKENS then
if column <= margin then
unget
break
end
list_marker = case type
when :BULLET then '*'
when :LABEL then "[#{data}]"
when :LALPHA, :NUMBER, :UALPHA then "#{data}."
when :NOTE then "#{data}::"
end
verbatim << list_marker
_, data, = get
verbatim << ' ' * (data - list_marker.length)
when :NEWLINE then
verbatim << data
break unless [:INDENT, :NEWLINE].include? peek_token[0]
else
unget
break
end
end
verbatim.normalize
p :verbatim_end => margin if @debug
verbatim
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 a Document
def parse indent = 0
p :parse_start => indent if @debug
document = []
until @tokens.empty? do
type, data, column, = get
if type != :INDENT and column < indent then
unget
break
end
case type
when :HEADER then
document << build_heading(data)
when :INDENT then
if indent > data then
unget
break
elsif indent == data then
next
end
unget
document << build_verbatim(indent)
when :NEWLINE then
document << RDoc::Markup::BlankLine.new
skip :NEWLINE, false
when :RULE then
document << RDoc::Markup::Rule.new(data)
skip :NEWLINE
when :TEXT then
unget
document << build_paragraph(indent)
# we're done with this paragraph (indent mismatch)
break if peek_token[0] == :TEXT
when *LIST_TOKENS then
unget
list = build_list(indent)
document << list if list
# we're done with this list (indent mismatch)
break if LIST_TOKENS.include? peek_token.first and indent > 0
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
document
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
##
# Skips a token of +token_type+, optionally raising an error.
def skip token_type, error = true
type, data, = 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
##
# Consumes tokens until NEWLINE and turns them back into text
def text
text = ''
loop do
type, data, = get
text << case type
when :BULLET then
_, space, = get # SPACE
"*#{' ' * (space - 1)}"
when :LABEL then
_, space, = get # SPACE
"[#{data}]#{' ' * (space - data.length - 2)}"
when :LALPHA, :NUMBER, :UALPHA then
_, space, = get # SPACE
"#{data}.#{' ' * (space - 2)}"
when :NOTE then
_, space = get # SPACE
"#{data}::#{' ' * (space - data.length - 2)}"
when :TEXT then
data
when :NEWLINE then
unget
break
when nil then
break
else
raise ParseError, "unhandled token #{@current_token.inspect}"
end
end
text
end
##
# Calculates the column and line of the current token based on +offset+.
def token_pos offset
[offset - @line_pos, @line]
end
##
# Turns text +input+ into a stream of tokens
def tokenize input
s = StringScanner.new input
@line = 0
@line_pos = 0
until s.eos? do
pos = s.pos
@tokens << case
when s.scan(/\r?\n/) then
token = [:NEWLINE, s.matched, *token_pos(pos)]
@line_pos = s.pos
@line += 1
token
when s.scan(/ +/) then
[:INDENT, s.matched_size, *token_pos(pos)]
when s.scan(/(=+)\s+/) then
level = s[1].length
level = 6 if level > 6
@tokens << [:HEADER, level, *token_pos(pos)]
pos = s.pos
s.scan(/.*/)
[:TEXT, s.matched, *token_pos(pos)]
when s.scan(/^(-{3,}) *$/) then
[:RULE, s[1].length - 2, *token_pos(pos)]
when s.scan(/([*-])\s+/) then
@tokens << [:BULLET, :BULLET, *token_pos(pos)]
[:SPACE, s.matched_size, *token_pos(pos)]
when s.scan(/([a-z]|\d+)\.[ \t]+\S/i) then
list_label = s[1]
width = s.matched_size - 1
s.pos -= 1 # 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
@tokens << [list_type, list_label, *token_pos(pos)]
[:SPACE, width, *token_pos(pos)]
when s.scan(/\[(.*?)\]( +|$)/) then
@tokens << [:LABEL, s[1], *token_pos(pos)]
[:SPACE, s.matched_size, *token_pos(pos)]
when s.scan(/(.*?)::( +|$)/) then
@tokens << [:NOTE, s[1], *token_pos(pos)]
[:SPACE, s.matched_size, *token_pos(pos)]
else s.scan(/.*/)
[:TEXT, s.matched, *token_pos(pos)]
end
end
self
end
##
# Returns the current token or +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
require 'rdoc/markup/blank_line'
require 'rdoc/markup/document'
require 'rdoc/markup/heading'
require 'rdoc/markup/list'
require 'rdoc/markup/list_item'
require 'rdoc/markup/paragraph'
require 'rdoc/markup/rule'
require 'rdoc/markup/verbatim'

View file

@ -7,6 +7,10 @@ require 'rdoc/markup'
class RDoc::Markup::PreProcess
##
# 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
@ -44,15 +48,16 @@ class RDoc::Markup::PreProcess
def include_file(name, indent)
if full_name = find_include_file(name) then
content = File.open(full_name) {|f| f.read}
content = File.read full_name
# strip leading '#'s, but only if all lines start with them
if content =~ /^[^#]/
if content =~ /^[^#]/ then
content.gsub(/^/, indent)
else
content.gsub(/^#?/, indent)
end
else
$stderr.puts "Couldn't find file to include: '#{name}'"
$stderr.puts "Couldn't find file to include '#{name}' from #{@input_file_name}"
''
end
end

17
lib/rdoc/markup/rule.rb Normal file
View file

@ -0,0 +1,17 @@
##
# A horizontal rule with a weight
class RDoc::Markup::Rule < Struct.new :weight
def accept visitor
visitor.accept_rule self
end
def pretty_print q # :nodoc:
q.group 2, '[rule:', ']' do
q.pp weight
end
end
end

View file

@ -0,0 +1,72 @@
require 'rdoc/markup/inline'
##
# Outputs RDoc markup with vibrant ANSI color!
class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
def initialize
super
@headings.clear
@headings[1] = ["\e[1;32m", "\e[m"]
@headings[2] = ["\e[4;32m", "\e[m"]
@headings[3] = ["\e[32m", "\e[m"]
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
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
2
when :NOTE, :LABEL then
@res << "\n"
2
else
bullet = @list_index.last.to_s
@list_index[-1] = @list_index.last.succ
bullet.length + 2
end
@indent -= width
end
def accept_list_item_start list_item
bullet = case @list_type.last
when :BULLET then
'*'
when :NOTE, :LABEL then
attributes(list_item.label) + ":\n"
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
def start_accepting
super
@res = ["\e[0m"]
end
end

74
lib/rdoc/markup/to_bs.rb Normal file
View file

@ -0,0 +1,74 @@
require 'rdoc/markup/inline'
##
# 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
def initialize
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, '+_', '-_'
end
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

View file

@ -1,185 +0,0 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
class RDoc::Markup
module Flow
P = Struct.new(:body)
VERB = Struct.new(:body)
RULE = Struct.new(:width)
class LIST
attr_reader :type, :contents
def initialize(type)
@type = type
@contents = []
end
def <<(stuff)
@contents << stuff
end
end
LI = Struct.new(:label, :body)
H = Struct.new(:level, :text)
end
class ToFlow < RDoc::Markup::Formatter
LIST_TYPE_TO_HTML = {
:BULLET => [ "<ul>", "</ul>" ],
:NUMBER => [ "<ol>", "</ol>" ],
:UPPERALPHA => [ "<ol>", "</ol>" ],
:LOWERALPHA => [ "<ol>", "</ol>" ],
:LABELED => [ "<dl>", "</dl>" ],
:NOTE => [ "<table>", "</table>" ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
super
init_tags
end
##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@attr_tags = [
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
]
end
##
# Add a new set of HTML tags for an attribute. We allow separate start and
# end tags for flexibility
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##
# Given an HTML tag, decorate it with class information and the like if
# required. This is a no-op in the base class, but is overridden in HTML
# output classes that implement style sheets
def annotate(tag)
tag
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = []
@list_stack = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
end
def accept_verbatim(am, fragment)
@res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << Flow::RULE.new(size)
end
def accept_list_start(am, fragment)
@list_stack.push(@res)
list = Flow::LIST.new(fragment.type)
@res << list
@res = list
end
def accept_list_end(am, fragment)
@res = @list_stack.pop
end
def accept_list_item(am, fragment)
@res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
end
def accept_blank_line(am, fragment)
# @res << annotate("<p />") << "\n"
end
def accept_heading(am, fragment)
@res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
end
private
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
res << annotate(tag.on)
end
end
end
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
res << annotate(tag.off)
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
def convert_string(item)
CGI.escapeHTML(item)
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
end
end

View file

@ -1,38 +1,28 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
##
# Outputs RDoc markup as HTML
class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
##
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
LIST_TYPE_TO_HTML = {
:BULLET => %w[<ul> </ul>],
:NUMBER => %w[<ol> </ol>],
:UPPERALPHA => %w[<ol> </ol>],
:LOWERALPHA => %w[<ol> </ol>],
:LABELED => %w[<dl> </dl>],
:NOTE => %w[<table> </table>],
:BULLET => ['<ul>', '</ul>'],
:LABEL => ['<dl>', '</dl>'],
:LALPHA => ['<ol style="display: lower-alpha">', '</ol>'],
:NOTE => ['<table>', '</table>'],
:NUMBER => ['<ol>', '</ol>'],
:UALPHA => ['<ol style="display: upper-alpha">', '</ol>'],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
super
# @in_tt - tt nested levels count
# @tt_bit - cache
@in_tt = 0
@tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
# external hyperlinks
@markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
# and links of the form <text>[<url>]
@markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
init_tags
end
attr_reader :res # :nodoc:
attr_reader :in_list_entry # :nodoc:
attr_reader :list # :nodoc:
##
# Converts a target url to one that is relative to a given path
@ -44,6 +34,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
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
@ -55,6 +48,31 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
File.join(*from)
end
def initialize
super
@th = nil
@in_list_entry = nil
@list = nil
# external hyperlinks
@markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
# and links of the form <text>[<url>]
@markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
init_tags
end
##
# Maps attributes to HTML tags
def init_tags
add_tag :BOLD, "<b>", "</b>"
add_tag :TT, "<tt>", "</tt>"
add_tag :EM, "<em>", "</em>"
end
##
# Generate a hyperlink for url, labeled with text. Handle the
# special cases for img: and link: described under handle_special_HYPERLINK
@ -112,117 +130,14 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
gen_url url, label
end
##
# are we currently inside <tt> tags?
def in_tt?
@in_tt > 0
end
##
# is +tag+ a <tt> tag?
def tt?(tag)
tag.bit == @tt_bit
end
##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@attr_tags = [
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
]
end
##
# Add a new set of HTML tags for an attribute. We allow separate start and
# end tags for flexibility.
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##
# Given an HTML tag, decorate it with class information and the like if
# required. This is a no-op in the base class, but is overridden in HTML
# output classes that implement style sheets.
def annotate(tag)
tag
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << annotate("<p>") + "\n"
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << annotate("</p>") + "\n"
end
def accept_verbatim(am, fragment)
@res << annotate("<pre>") + "\n"
@res << CGI.escapeHTML(fragment.txt)
@res << annotate("</pre>") << "\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "<hr size=\"#{size}\"></hr>"
end
def accept_list_start(am, fragment)
@res << html_list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << annotate(tag) << "\n"
end
@res << html_list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << annotate(tag) << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << annotate("<p />") << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
res = []
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
@ -243,65 +158,94 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
res.join
end
##
# :section: Visitor
def start_accepting
@res = []
@in_list_entry = []
@list = []
end
def end_accepting
@res.join
end
def accept_paragraph(paragraph)
@res << annotate("<p>") + "\n"
@res << wrap(convert_flow(@am.flow(paragraph.text)))
@res << annotate("</p>") + "\n"
end
def accept_verbatim(verbatim)
@res << annotate("<pre>") << "\n"
@res << CGI.escapeHTML(verbatim.text)
@res << annotate("</pre>") << "\n"
end
def accept_rule(rule)
size = rule.weight
size = 10 if size > 10
@res << "<hr style=\"height: #{size}px\"></hr>"
end
def accept_list_start(list)
@list << list.type
@res << html_list_name(list.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(list)
@list.pop
if tag = @in_list_entry.pop
@res << annotate(tag) << "\n"
end
@res << html_list_name(list.type, false) << "\n"
end
def accept_list_item_start(list_item)
if tag = @in_list_entry.last
@res << annotate(tag) << "\n"
end
@res << list_item_start(list_item, @list.last)
end
def accept_list_item_end(list_item)
@in_list_entry[-1] = list_end_for(@list.last)
end
def accept_blank_line(blank_line)
# @res << annotate("<p />") << "\n"
end
def accept_heading(heading)
@res << convert_heading(heading.level, @am.flow(heading.text))
end
private
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
res << annotate(tag.on)
@in_tt += 1 if tt?(tag)
end
end
end
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
@in_tt -= 1 if tt?(tag)
res << annotate(tag.off)
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
res << convert_string(item)
when RDoc::Markup::AttrChanger
off_tags(res, item)
on_tags(res, item)
when RDoc::Markup::Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# Converts string +item+
def convert_string(item)
in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
end
##
# Escapes HTML in +item+
def convert_string_simple(item)
CGI.escapeHTML item
end
##
# some of these patterns are taken from SmartyPants...
# Converts ampersand, dashes, elipsis, quotes, copyright and registered
# trademark symbols to HTML escaped Unicode.
def convert_string_fancy(item)
# convert ampersand before doing anything else
@ -333,69 +277,62 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
gsub(/\(r\)/, '&#174;')
end
def convert_special(special)
handled = false
RDoc::Markup::Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
##
# Converts headings to hN elements
def convert_heading(level, flow)
res =
annotate("<h#{level}>") +
convert_flow(flow) +
annotate("</h#{level}>\n")
[annotate("<h#{level}>"),
convert_flow(flow),
annotate("</h#{level}>\n")].join
end
def html_list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
annotate(tags[ is_open_tag ? 0 : 1])
##
# Determins 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
annotate tags[open_tag ? 0 : 1]
end
def list_item_start(am, fragment)
case fragment.type
when :BULLET, :NUMBER then
##
# Starts a list item
def list_item_start(list_item, list_type)
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
annotate("<li>")
when :UPPERALPHA then
annotate("<li type=\"A\">")
when :LOWERALPHA then
annotate("<li type=\"a\">")
when :LABELED then
when :LABEL then
annotate("<dt>") +
convert_flow(am.flow(fragment.param)) +
convert_flow(@am.flow(list_item.label)) +
annotate("</dt>") +
annotate("<dd>")
when :NOTE then
annotate("<tr>") +
annotate("<td valign=\"top\">") +
convert_flow(am.flow(fragment.param)) +
convert_flow(@am.flow(list_item.label)) +
annotate("</td>") +
annotate("<td>")
else
raise "Invalid list type"
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
def list_end_for(fragment_type)
case fragment_type
when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
##
# Ends a list item
def list_end_for(list_type)
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
"</li>"
when :LABELED then
when :LABEL then
"</dd>"
when :NOTE then
"</td></tr>"
else
raise "Invalid list type"
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end

View file

@ -1,44 +1,35 @@
require 'rdoc/markup/to_html'
##
# Subclass of the RDoc::Markup::ToHtml class that supports looking up words in
# the AllReferences list. Those that are found (like AllReferences in this
# comment) will be hyperlinked
# Subclass of the RDoc::Markup::ToHtml class that supports looking up words
# from a context. Those that are found will be hyperlinked.
class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
attr_accessor :context
##
# Regular expression to match class references
#
# 1) There can be a '\' in front of text to suppress any cross-references
# 2) There can be a '::' in front of class names to reference from the
# top-level namespace.
# 3) The method can be followed by parenthesis
# Regular expressions to match class and method references.
#
# 1.) There can be a '\' in front of text to suppress
# any cross-references (note, however, that the single '\'
# is written as '\\\\' in order to escape it twice, once
# in the Ruby String literal and once in the regexp).
# 2.) There can be a '::' in front of class names to reference
# from the top-level namespace.
# 3.) The method can be followed by parenthesis,
# which may or may not have things inside (this
# apparently is allowed for Fortran 95, but I also think that this
# is a good idea for Ruby, as it is very reasonable to want to
# reference a call with arguments).
#
# NOTE: In order to support Fortran 95 properly, the [A-Z] below
# should be changed to [A-Za-z]. This slows down rdoc significantly,
# however, and the Fortran 95 support is broken in any case due to
# the return in handle_special_CROSSREF if the token consists
# entirely of lowercase letters.
#
# The markup/cross-referencing engine needs a rewrite for
# Fortran 95 to be supported properly.
CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
METHOD_REGEXP_STR = '(\w+[!?=]?)(?:\([\.\w+\*\/\+\-\=\<\>]*\))?'
##
# Regular expression to match method references.
#
# See CLASS_REGEXP_STR
METHOD_REGEXP_STR = '(\w+[!?=]?)(?:\([\w.+*/=<>-]*\))?'
##
# Regular expressions matching text that should potentially have
# cross-reference links generated are passed to add_special.
# Note that these expressions are meant to pick up text for which
# cross-references have been suppressed, since the suppression
# characters are removed by the code that is triggered.
# cross-reference links generated are passed to add_special. Note that
# these expressions are meant to pick up text for which cross-references
# have been suppressed, since the suppression characters are removed by the
# code that is triggered.
CROSSREF_REGEXP = /(
# A::B::C.meth
#{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}
@ -72,8 +63,14 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
)/x
##
# We need to record the html path of our caller so we can generate
# correct relative paths for any hyperlinks that we find
# RDoc::CodeObject for generating references
attr_accessor :context
##
# 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.
def initialize(from_path, context, show_hash)
raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
@ -89,19 +86,18 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
end
##
# We're invoked when any text matches the CROSSREF pattern
# (defined in MarkUp). If we fine the corresponding reference,
# generate a hyperlink. If the name we're looking for contains
# no punctuation, we look for it up the module/class chain. For
# example, HyperlinkHtml is found, even without the Generator::
# prefix, because we look for it in module Generator first.
# We're invoked when any text matches the CROSSREF pattern (defined in
# MarkUp). If we find the corresponding reference, generate a hyperlink.
# If the name we're looking for contains no punctuation, we look for it up
# the module/class chain. For example, HyperlinkHtml is found, even without
# the Generator:: prefix, because we look for it in module Generator first.
def handle_special_CROSSREF(special)
name = special.text
# 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)
# 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/
return @seen[name] if @seen.include? name
@ -113,7 +109,6 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
lookup = name
end
# Find class, module, or method in class or module.
#
# Do not, however, use an if/elsif/else chain to do so. Instead, test
@ -132,7 +127,9 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
ref = @context.find_symbol lookup unless ref
out = if lookup =~ /^\\/ then
out = if lookup == '\\' then
lookup
elsif lookup =~ /^\\/ then
$'
elsif ref and ref.document_self then
"<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
@ -146,3 +143,4 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
end
end

View file

@ -1,328 +0,0 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
##
# Convert SimpleMarkup to basic LaTeX report format.
class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter
BS = "\020" # \
OB = "\021" # {
CB = "\022" # }
DL = "\023" # Dollar
BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
HAT = "#{BS}symbol#{OB}94#{CB}"
BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
TILDE = "#{DL}#{BS}sim#{DL}"
LESSTHAN = "#{DL}<#{DL}"
GREATERTHAN = "#{DL}>#{DL}"
def self.l(str)
str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
end
def l(arg)
RDoc::Markup::ToLaTeX.l(arg)
end
LIST_TYPE_TO_LATEX = {
:BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
:NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
:UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
:LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
:LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
:NOTE => [
l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
l("\\end{tabularx}") ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
@list_depth = 0
@prev_list_types = []
end
##
# Set up the standard mapping of attributes to LaTeX
def init_tags
@attr_tags = [
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
]
end
##
# Escape a LaTeX string
def escape(str)
$stderr.print "FE: ", str if $DEBUG_RDOC
s = str.
sub(/\s+$/, '').
gsub(/([_\${}&%#])/, "#{BS}\\1").
gsub(/\\/, BACKSLASH).
gsub(/\^/, HAT).
gsub(/~/, TILDE).
gsub(/</, LESSTHAN).
gsub(/>/, GREATERTHAN).
gsub(/,,/, ",{},").
gsub(/\`/, BACKQUOTE)
$stderr.print "-> ", s, "\n" if $DEBUG_RDOC
s
end
##
# Add a new set of LaTeX tags for an attribute. We allow
# separate start and end tags for flexibility
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
end
def accept_paragraph(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << "\n"
end
def accept_verbatim(am, fragment)
@res << "\n\\begin{code}\n"
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
@res << "\n\\end{code}\n\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
end
def accept_list_start(am, fragment)
@res << list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << tag << "\n"
end
@res << list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << tag << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
end
private
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
res << tag.on
end
end
end
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
res << tag.off
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
$stderr.puts "Converting '#{item}'" if $DEBUG_RDOC
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# some of these patterns are taken from SmartyPants...
def convert_string(item)
escape(item).
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1\'').
gsub(%r{\'(?=\W|s\b)}, "'" ).
# convert single opening quote
gsub(/'/, '`').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, "\\1''").
# convert double opening quote
gsub(/"/, "``").
# convert copyright
gsub(/\(c\)/, '\copyright{}')
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
case level
when 1 then "\\chapter{"
when 2 then "\\section{"
when 3 then "\\subsection{"
when 4 then "\\subsubsection{"
else "\\paragraph{"
end +
convert_flow(flow) +
"}\n"
end
def list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
if tags[2] # enumerate
if is_open_tag
@list_depth += 1
if @prev_list_types[@list_depth] != tags[2]
case @list_depth
when 1
roman = "i"
when 2
roman = "ii"
when 3
roman = "iii"
when 4
roman = "iv"
else
raise("Too deep list: level #{@list_depth}")
end
@prev_list_types[@list_depth] = tags[2]
return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
end
else
@list_depth -= 1
end
end
tags[ is_open_tag ? 0 : 1]
end
def list_item_start(am, fragment)
case fragment.type
when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
"\\item "
when :LABELED then
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
when :NOTE then
convert_flow(am.flow(fragment.param)) + " & "
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA, :LABELED then
""
when :NOTE
"\\\\\n"
else
raise "Invalid list type"
end
end
end

243
lib/rdoc/markup/to_rdoc.rb Normal file
View file

@ -0,0 +1,243 @@
require 'rdoc/markup/inline'
##
# Outputs RDoc markup as RDoc markup! (mostly)
class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
attr_accessor :indent
attr_reader :list_index
attr_reader :list_type
attr_reader :list_width
attr_reader :prefix
attr_reader :res
def initialize
super
@markup.add_special(/\\[^\s]/, :SUPPRESSED_CROSSREF)
@width = 78
@prefix = ''
init_tags
@headings = {}
@headings.default = []
@headings[1] = ['= ', '']
@headings[2] = ['== ', '']
@headings[3] = ['=== ', '']
@headings[4] = ['==== ', '']
@headings[5] = ['===== ', '']
@headings[6] = ['====== ', '']
end
##
# Maps attributes to ANSI sequences
def init_tags
add_tag :BOLD, "<b>", "</b>"
add_tag :TT, "<tt>", "</tt>"
add_tag :EM, "<em>", "</em>"
end
def accept_blank_line blank_line
@res << "\n"
end
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
def accept_list_end list
@list_index.pop
@list_type.pop
@list_width.pop
end
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
2
when :NOTE, :LABEL then
@res << "\n"
2
else
bullet = @list_index.last.to_s
@list_index[-1] = @list_index.last.succ
bullet.length + 2
end
@indent -= width
end
def accept_list_item_start list_item
bullet = case @list_type.last
when :BULLET then
'*'
when :NOTE, :LABEL then
attributes(list_item.label) + ":\n"
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.length + 1
@indent += width
end
end
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
def accept_paragraph paragraph
wrap attributes(paragraph.text)
end
def accept_rule rule
use_prefix or @res << ' ' * @indent
@res << '-' * (@width - @indent)
@res << "\n"
end
##
# Outputs +verbatim+ flush left and indented 2 columns
def accept_verbatim verbatim
indent = ' ' * (@indent + 2)
lines = []
current_line = []
# split into lines
verbatim.parts.each do |part|
current_line << part
if part == "\n" then
lines << current_line
current_line = []
end
end
lines << current_line unless current_line.empty?
# calculate margin
indented = lines.select { |line| line != ["\n"] }
margin = indented.map { |line| line.first.length }.min
# flush left
indented.each { |line| line[0][0...margin] = '' }
# output
use_prefix or @res << indent # verbatim is unlikely to have prefix
@res << lines.shift.join
lines.each do |line|
@res << indent unless line == ["\n"]
@res << line.join
end
@res << "\n"
end
def attributes text
flow = @am.flow text.dup
convert_flow flow
end
def end_accepting
@res.join
end
def handle_special_SUPPRESSED_CROSSREF special
special.text.sub(/\\/, '')
end
def start_accepting
@res = [""]
@indent = 0
@prefix = nil
@list_index = []
@list_type = []
@list_width = []
end
def use_prefix
prefix = @prefix
@prefix = nil
@res << prefix if prefix
prefix
end
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

View file

@ -6,44 +6,58 @@ require 'rdoc/markup/formatter'
class RDoc::Markup::ToTest < RDoc::Markup::Formatter
##
# :section: Visitor
def start_accepting
@res = []
@list = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << fragment.to_s
def accept_paragraph(paragraph)
@res << paragraph.text
end
def accept_verbatim(am, fragment)
@res << fragment.to_s
def accept_verbatim(verbatim)
@res << verbatim.text
end
def accept_list_start(am, fragment)
@res << fragment.to_s
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(am, fragment)
@res << fragment.to_s
def accept_list_end(list)
@list.pop
end
def accept_list_item(am, fragment)
@res << fragment.to_s
def accept_list_item_start(list_item)
@res << "#{' ' * (@list.size - 1)}#{@list.last}: "
end
def accept_blank_line(am, fragment)
@res << fragment.to_s
def accept_list_item_end(list_item)
end
def accept_heading(am, fragment)
@res << fragment.to_s
def accept_blank_line(blank_line)
@res << "\n"
end
def accept_rule(am, fragment)
@res << fragment.to_s
def accept_heading(heading)
@res << "#{'=' * heading.level} #{heading.text}"
end
def accept_rule(rule)
@res << '-' * rule.weight
end
end

View file

@ -1,69 +0,0 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'rdoc/markup'
require 'rdoc/markup/formatter'
##
# Convert SimpleMarkup to basic TexInfo format
#
# TODO: WTF is AttributeManager for?
#
class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter
def start_accepting
@text = []
end
def end_accepting
@text.join("\n")
end
def accept_paragraph(attributes, text)
@text << format(text)
end
def accept_verbatim(attributes, text)
@text << "@verb{|#{format(text)}|}"
end
def accept_heading(attributes, text)
heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading'
@text << "#{heading} #{format(text)}"
end
def accept_list_start(attributes, text)
@text << '@itemize @bullet'
end
def accept_list_end(attributes, text)
@text << '@end itemize'
end
def accept_list_item(attributes, text)
@text << "@item\n#{format(text)}"
end
def accept_blank_line(attributes, text)
@text << "\n"
end
def accept_rule(attributes, text)
@text << '-----'
end
def format(text)
text.txt.
gsub(/@/, "@@").
gsub(/\{/, "@{").
gsub(/\}/, "@}").
# gsub(/,/, "@,"). # technically only required in cross-refs
gsub(/\+([\w]+)\+/, "@code{\\1}").
gsub(/\<tt\>([^<]+)\<\/tt\>/, "@code{\\1}").
gsub(/\*([\w]+)\*/, "@strong{\\1}").
gsub(/\<b\>([^<]+)\<\/b\>/, "@strong{\\1}").
gsub(/_([\w]+)_/, "@emph{\\1}").
gsub(/\<em\>([^<]+)\<\/em\>/, "@emph{\\1}")
end
end

View file

@ -0,0 +1,42 @@
##
# A section of verbatim text
class RDoc::Markup::Verbatim < RDoc::Markup::Paragraph
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 /\n/ then
newlines += 1
parts << part if newlines <= 2
else
newlines = 0
parts << part
end
end
parts.slice!(-1) if parts[-2..-1] == ["\n", "\n"]
@parts = parts
end
##
# The text of the section
def text
@parts.join
end
end

8
lib/rdoc/meta_method.rb Normal file
View file

@ -0,0 +1,8 @@
require 'rdoc/any_method'
##
# MetaMethod represents a meta-programmed method
class RDoc::MetaMethod < RDoc::AnyMethod
end

55
lib/rdoc/normal_class.rb Normal file
View file

@ -0,0 +1,55 @@
require 'rdoc/class_module'
##
# A normal class, neither singleton nor anonymous
class RDoc::NormalClass < RDoc::ClassModule
##
# Ancestor ClassModules
def ancestors
includes + [superclass]
end
def inspect # :nodoc:
superclass = @superclass ? " < #{@superclass}" : nil
"<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [
self.class, object_id,
full_name, superclass, @includes, @attributes, @method_list, @aliases
]
end
def pretty_print q # :nodoc:
superclass = @superclass ? " < #{@superclass}" : nil
q.group 2, "[class #{full_name}#{superclass} ", "]" do
q.breakable
q.text "includes:"
q.breakable
q.seplist @includes do |inc| q.pp inc end
q.breakable
q.text "attributes:"
q.breakable
q.seplist @attributes do |inc| q.pp inc end
q.breakable
q.text "methods:"
q.breakable
q.seplist @method_list do |inc| q.pp inc end
q.breakable
q.text "aliases:"
q.breakable
q.seplist @aliases do |inc| q.pp inc end
q.breakable
q.text "comment:"
q.breakable
q.pp comment
end
end
end

64
lib/rdoc/normal_module.rb Normal file
View file

@ -0,0 +1,64 @@
require 'rdoc/class_module'
##
# A normal module, like NormalClass
class RDoc::NormalModule < RDoc::ClassModule
##
# Included NormalModules
alias ancestors includes
def inspect # :nodoc:
"#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [
self.class, object_id,
full_name, @includes, @attributes, @method_list, @aliases
]
end
##
# This is a module, returns true
def module?
true
end
def pretty_print q # :nodoc:
q.group 2, "[module #{full_name}: ", "]" do
q.breakable
q.text "includes:"
q.breakable
q.seplist @includes do |inc| q.pp inc end
q.breakable
q.text "attributes:"
q.breakable
q.seplist @attributes do |inc| q.pp inc end
q.breakable
q.text "methods:"
q.breakable
q.seplist @method_list do |inc| q.pp inc end
q.breakable
q.text "aliases:"
q.breakable
q.seplist @aliases do |inc| q.pp inc end
q.breakable
q.text "comment:"
q.breakable
q.pp comment
end
end
##
# Modules don't have one, raises NoMethodError
def superclass
raise NoMethodError, "#{full_name} is a module"
end
end

View file

@ -1,51 +1,22 @@
# We handle the parsing of options, and subsequently as a singleton
# object to be queried for option values
require "rdoc/ri/paths"
require 'optparse'
require 'rdoc/ri/paths'
##
# RDoc::Options handles the parsing and storage of options
class RDoc::Options
##
# Should the output be placed into a single file
attr_reader :all_one_file
##
# Character-set
attr_reader :charset
##
# URL of stylesheet
attr_reader :css
##
# Should diagrams be drawn
attr_reader :diagram
##
# Files matching this pattern will be excluded
attr_accessor :exclude
##
# Additional attr_... style method flags
attr_reader :extra_accessor_flags
##
# Pattern for additional attr_... style methods
attr_accessor :extra_accessors
##
# Should we draw fileboxes in diagrams
attr_reader :fileboxes
##
# The list of files to be processed
@ -66,46 +37,21 @@ class RDoc::Options
attr_accessor :formatter
##
# image format for diagrams
attr_reader :image_format
##
# Include line numbers in the source listings
attr_reader :include_line_numbers
##
# Should source code be included inline, or displayed in a popup
attr_accessor :inline_source
##
# Name of the file, class or module to display in the initial index page (if
# not specified the first file we encounter is used)
attr_accessor :main_page
##
# Merge into classes of the same name when generating ri
attr_reader :merge
##
# The name of the output directory
attr_accessor :op_dir
##
# The name to use for the output
# Is RDoc in pipe mode?
attr_accessor :op_name
##
# Are we promiscuous about showing module contents across multiple files
attr_reader :promiscuous
attr_accessor :pipe
##
# Array of directories to search for files to satisfy an :include:
@ -113,7 +59,7 @@ class RDoc::Options
attr_reader :rdoc_include
##
# Include private and protected methods in the output
# Include private and protected methods in the output?
attr_accessor :show_all
@ -128,17 +74,10 @@ class RDoc::Options
attr_reader :tab_width
##
# template to be used when generating output
# Template to be used when generating output
attr_reader :template
##
# Template class for file generation
#--
# HACK around dependencies in lib/rdoc/generator/html.rb
attr_accessor :template_class # :nodoc:
##
# Documentation title
@ -154,34 +93,24 @@ class RDoc::Options
attr_reader :webcvs
def initialize(generators = {}) # :nodoc:
@op_dir = "doc"
@op_name = nil
def initialize # :nodoc:
require 'rdoc/rdoc'
@op_dir = nil
@show_all = false
@main_page = nil
@merge = false
@exclude = []
@generators = generators
@generator_name = 'html'
@generator = @generators[@generator_name]
@generators = RDoc::RDoc::GENERATORS
@generator = RDoc::Generator::Darkfish
@generator_name = nil
@rdoc_include = []
@title = nil
@template = nil
@template_class = nil
@diagram = false
@fileboxes = false
@show_hash = false
@image_format = 'png'
@inline_source = false
@all_one_file = false
@tab_width = 8
@include_line_numbers = false
@extra_accessor_flags = {}
@promiscuous = false
@force_update = false
@force_update = true
@verbosity = 1
@pipe = false
@css = nil
@webcvs = nil
@charset = 'utf-8'
@ -191,7 +120,7 @@ class RDoc::Options
# Parse command line options.
def parse(argv)
accessors = []
ignore_invalid = true
opts = OptionParser.new do |opt|
opt.program_name = File.basename $0
@ -210,43 +139,25 @@ Usage: #{opt.program_name} [options] [names...]
How RDoc generates output depends on the output formatter being used, and on
the options you give.
- HTML output is normally produced into a number of separate files
(one per class, module, and file, along with various indices).
These files will appear in the directory given by the --op
option (doc/ by default).
- Darkfish creates frameless HTML output by Michael Granger.
- ri creates ri data files
- XML output by default is written to standard output. If a
--opname option is given, the output will instead be written
to a file with that name in the output directory.
RDoc understands the following file formats:
- .chm files (Windows help files) are written in the --op directory.
If an --opname parameter is present, that name is used, otherwise
the file will be called rdoc.chm.
EOF
opt.separator nil
opt.separator "Options:"
opt.separator nil
parsers = Hash.new { |h,parser| h[parser] = [] }
opt.on("--accessor=ACCESSORS", "-A", Array,
"A comma separated list of additional class",
"methods that should be treated like",
"'attr_reader' and friends.",
" ",
"Option may be repeated.",
" ",
"Each accessorname may have '=text'",
"appended, in which case that text appears",
"where the r/w/rw appears for normal.",
"accessors") do |value|
value.each do |accessor|
if accessor =~ /^(\w+)(=(.*))?$/
accessors << $1
@extra_accessor_flags[$1] = $3
end
end
RDoc::Parser.parsers.each do |regexp, parser|
parsers[parser.name.sub('RDoc::Parser::', '')] << regexp.source
end
parsers.sort.each do |parser, regexp|
opt.banner << " - #{parser}: #{regexp.join ', '}\n"
end
opt.separator nil
opt.separator "Parsing Options:"
opt.separator nil
opt.on("--all", "-a",
@ -257,31 +168,6 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
opt.on("--charset=CHARSET", "-c",
"Specifies the output HTML character-set.") do |value|
@charset = value
end
opt.separator nil
opt.on("--debug", "-D",
"Displays lots on internal stuff.") do |value|
$DEBUG_RDOC = value
end
opt.separator nil
opt.on("--diagram", "-d",
"Generate diagrams showing modules and",
"classes. You need dot V1.8.6 or later to",
"use the --diagram option correctly. Dot is",
"available from http://graphviz.org") do |value|
check_diagram
@diagram = true
end
opt.separator nil
opt.on("--exclude=PATTERN", "-x", Regexp,
"Do not process files or directories",
"matching PATTERN.") do |value|
@ -300,25 +186,13 @@ Usage: #{opt.program_name} [options] [names...]
raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
end
unless RDoc::ParserFactory.alias_extension old, new then
unless RDoc::Parser.alias_extension old, new then
raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
end
end
opt.separator nil
opt.on("--fileboxes", "-F",
"Classes are put in boxes which represents",
"files, where these classes reside. Classes",
"shared between more than one file are",
"shown with list of files that are sharing",
"them. Silently discarded if --diagram is",
"not given.") do |value|
@fileboxes = value
end
opt.separator nil
opt.on("--force-update", "-U",
"Forces rdoc to scan all sources even if",
"newer than the flag file.") do |value|
@ -327,27 +201,34 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
opt.on("--pipe",
"Convert RDoc on stdin to HTML") do
@pipe = true
end
opt.separator nil
opt.separator "Generator Options:"
opt.separator nil
opt.on("--charset=CHARSET", "-c",
"Specifies the output HTML character-set.") do |value|
@charset = value
end
opt.separator nil
generator_text = @generators.keys.map { |name| " #{name}" }.sort
opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", @generators.keys,
"Set the output formatter.") do |value|
"Set the output formatter. One of:", *generator_text) do |value|
@generator_name = value.downcase
setup_generator
end
opt.separator nil
image_formats = %w[gif png jpg jpeg]
opt.on("--image-format=FORMAT", "-I", image_formats,
"Sets output image format for diagrams. Can",
"be #{image_formats.join ', '}. If this option",
"is omitted, png is used. Requires",
"diagrams.") do |value|
@image_format = value
end
opt.separator nil
opt.on("--include=DIRECTORIES", "-i", Array,
"set (or add to) the list of directories to",
"Set (or add to) the list of directories to",
"be searched when satisfying :include:",
"requests. Can be used more than once.") do |value|
@rdoc_include.concat value.map { |dir| dir.strip }
@ -355,21 +236,6 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
opt.on("--inline-source", "-S",
"Show method source code inline, rather than",
"via a popup link.") do |value|
@inline_source = value
end
opt.separator nil
opt.on("--line-numbers", "-N",
"Include line numbers in the source code.") do |value|
@include_line_numbers = value
end
opt.separator nil
opt.on("--main=NAME", "-m",
"NAME will be the initial page displayed.") do |value|
@main_page = value
@ -377,102 +243,13 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
opt.on("--merge", "-M",
"When creating ri output, merge previously",
"processed classes into previously",
"documented classes of the same name.") do |value|
@merge = value
end
opt.separator nil
opt.on("--one-file", "-1",
"Put all the output into a single file.") do |value|
@all_one_file = value
@inline_source = value if value
@template = 'one_page_html'
end
opt.separator nil
opt.on("--op=DIR", "-o",
opt.on("--output=DIR", "--op", "-o",
"Set the output directory.") do |value|
@op_dir = value
end
opt.separator nil
opt.on("--opname=NAME", "-n",
"Set the NAME of the output. Has no effect",
"for HTML.") do |value|
@op_name = value
end
opt.separator nil
opt.on("--promiscuous", "-p",
"When documenting a file that contains a",
"module or class also defined in other",
"files, show all stuff for that module or",
"class in each files page. By default, only",
"show stuff defined in that particular file.") do |value|
@promiscuous = value
end
opt.separator nil
opt.on("--quiet", "-q",
"Don't show progress as we parse.") do |value|
@verbosity = 0
end
opt.on("--verbose", "-v",
"Display extra progress as we parse.") do |value|
@verbosity = 2
end
opt.separator nil
opt.on("--ri", "-r",
"Generate output for use by `ri`. The files",
"are stored in the '.rdoc' directory under",
"your home directory unless overridden by a",
"subsequent --op parameter, so no special",
"privileges are needed.") do |value|
@generator_name = "ri"
@op_dir = RDoc::RI::Paths::HOMEDIR
setup_generator
end
opt.separator nil
opt.on("--ri-site", "-R",
"Generate output for use by `ri`. The files",
"are stored in a site-wide directory,",
"making them accessible to others, so",
"special privileges are needed.") do |value|
@generator_name = "ri"
@op_dir = RDoc::RI::Paths::SITEDIR
setup_generator
end
opt.separator nil
opt.on("--ri-system", "-Y",
"Generate output for use by `ri`. The files",
"are stored in a site-wide directory,",
"making them accessible to others, so",
"special privileges are needed. This",
"option is intended to be used during Ruby",
"installation.") do |value|
@generator_name = "ri"
@op_dir = RDoc::RI::Paths::SYSDIR
setup_generator
end
opt.separator nil
opt.on("--show-hash", "-H",
"A name of the form #name in a comment is a",
"possible hyperlink to an instance method",
@ -483,13 +260,6 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
opt.on("--style=URL", "-s",
"Specifies the URL of a separate stylesheet.") do |value|
@css = value
end
opt.separator nil
opt.on("--tab-width=WIDTH", "-w", OptionParser::DecimalInteger,
"Set the width of tab characters.") do |value|
@tab_width = value
@ -520,12 +290,82 @@ Usage: #{opt.program_name} [options] [names...]
"'\%s', the filename will be appended to it.") do |value|
@webcvs = value
end
opt.separator nil
opt.on("-d", "--diagram", "Prevents -d from tripping --debug")
opt.separator nil
opt.separator "ri Generator Options:"
opt.separator nil
opt.on("--ri", "-r",
"Generate output for use by `ri`. The files",
"are stored in the '.rdoc' directory under",
"your home directory unless overridden by a",
"subsequent --op parameter, so no special",
"privileges are needed.") do |value|
@generator_name = "ri"
@op_dir ||= RDoc::RI::Paths::HOMEDIR
setup_generator
end
opt.separator nil
opt.on("--ri-site", "-R",
"Generate output for use by `ri`. The files",
"are stored in a site-wide directory,",
"making them accessible to others, so",
"special privileges are needed.") do |value|
@generator_name = "ri"
@op_dir = RDoc::RI::Paths::SITEDIR
setup_generator
end
opt.separator nil
opt.separator "Generic Options:"
opt.separator nil
opt.on("-D", "--[no-]debug",
"Displays lots on internal stuff.") do |value|
$DEBUG_RDOC = value
end
opt.on("--[no-]ignore-invalid",
"Ignore invalid options and continue.") do |value|
ignore_invalid = value
end
opt.on("--quiet", "-q",
"Don't show progress as we parse.") do |value|
@verbosity = 0
end
opt.on("--verbose", "-v",
"Display extra progress as we parse.") do |value|
@verbosity = 2
end
opt.separator nil
end
argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
opts.parse! argv
begin
opts.parse! argv
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
if ignore_invalid and not quiet then
$stderr.puts e
$stderr.puts '(invalid options are ignored)'
else
$stderr.puts opts
$stderr.puts
$stderr.puts e
exit 1
end
end
@op_dir ||= 'doc'
@files = argv.dup
@rdoc_include << "." if @rdoc_include.empty?
@ -542,18 +382,6 @@ Usage: #{opt.program_name} [options] [names...]
# formatter
@template ||= @generator_name
# Generate a regexp from the accessors
unless accessors.empty? then
re = '^(' + accessors.map { |a| Regexp.quote a }.join('|') + ')$'
@extra_accessors = Regexp.new re
end
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
puts opts
puts
puts e
exit 1
end
##
@ -586,42 +414,6 @@ Usage: #{opt.program_name} [options] [names...]
unless @generator then
raise OptionParser::InvalidArgument, "Invalid output formatter"
end
if @generator_name == "xml" then
@all_one_file = true
@inline_source = true
end
end
# Check that the right version of 'dot' is available. Unfortunately this
# doesn't work correctly under Windows NT, so we'll bypass the test under
# Windows.
def check_diagram
return if RUBY_PLATFORM =~ /mswin|cygwin|mingw|bccwin/
ok = false
ver = nil
IO.popen "dot -V 2>&1" do |io|
ver = io.read
if ver =~ /dot.+version(?:\s+gviz)?\s+(\d+)\.(\d+)/ then
ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8)
end
end
unless ok then
if ver =~ /^dot.+version/ then
$stderr.puts "Warning: You may need dot V1.8.6 or later to use\n",
"the --diagram option correctly. You have:\n\n ",
ver,
"\nDiagrams might have strange background colors.\n\n"
else
$stderr.puts "You need the 'dot' program to produce diagrams.",
"(see http://www.research.att.com/sw/tools/graphviz/)\n\n"
exit
end
end
end
##
@ -629,7 +421,7 @@ Usage: #{opt.program_name} [options] [names...]
def check_files
@files.each do |f|
stat = File.stat f
stat = File.stat f rescue next
raise RDoc::Error, "file '#{f}' not readable" unless stat.readable?
end
end

View file

@ -22,14 +22,14 @@ require 'rdoc/stats'
# following incantation
#
# require "rdoc/parser"
#
#
# class RDoc::Parser::Xyz < RDoc::Parser
# parse_files_matching /\.xyz$/ # <<<<
#
#
# def initialize(file_name, body, options)
# ...
# end
#
#
# def scan
# ...
# end
@ -63,13 +63,37 @@ class RDoc::Parser
end
##
# Return _true_ if the +file+ seems like binary.
# Determines if the file is a "binary" file which basically means it has
# content that an RDoc parser shouldn't try to consume.
def self.binary?(file)
s = File.read(file, 1024) or return false
s.count("^ -~\t\r\n").fdiv(s.size) > 0.3 || s.index("\x00")
s = File.read(file, File.stat(file).blksize) || ""
if s[0, 2] == Marshal.dump('')[0, 2] then
true
elsif file =~ /erb\.rb$/ then
false
elsif s.scan(/<%|%>/).length >= 4 then
true
else
# From ptools under the Artistic License 2.0, (c) Daniel Berger.
s = s.split(//)
((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
end
end
##
# Checks if +file+ is a zip file in disguise. Signatures from
# http://www.garykessler.net/library/file_sigs.html
def self.zip? file
zip_signature = File.read file, 4
zip_signature == "PK\x03\x04" or
zip_signature == "PK\x05\x06" or
zip_signature == "PK\x07\x08"
end
private_class_method :binary?
##
# Return a parser that can handle a particular extension
@ -77,16 +101,13 @@ class RDoc::Parser
def self.can_parse(file_name)
parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }.last
#
# The default parser should *NOT* parse binary files.
#
if parser == RDoc::Parser::Simple then
if binary? file_name then
return nil
end
end
# HACK Selenium hides a jar file using a .txt extension
return if parser == RDoc::Parser::Simple and zip? file_name
return parser
# The default parser must not parse binary files
return if parser == RDoc::Parser::Simple and file_name !~ /\.(txt|rdoc)$/
parser
end
##
@ -94,6 +115,8 @@ class RDoc::Parser
# for ones that we don't know
def self.for(top_level, file_name, body, options, stats)
return if binary? file_name
# If no extension, look for shebang
if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)} then
shebang = $1
@ -105,18 +128,15 @@ class RDoc::Parser
parser = can_parse file_name
#
# This method must return a parser.
#
if !parser then
parser = RDoc::Parser::Simple
end
return unless parser
parser.new top_level, file_name, body, options, stats
end
##
# Record which file types this parser can understand.
#
# It is ok to call this multiple times.
def self.parse_files_matching(regexp)
RDoc::Parser.parsers.unshift [regexp, self]

View file

@ -14,32 +14,32 @@ require 'rdoc/known_classes'
# method, that is to say the method whose name is given in the
# <tt>rb_define_method</tt> call. For example, you might write:
#
# /*
# * Returns a new array that is a one-dimensional flattening of this
# * array (recursively). That is, for every element that is an array,
# * extract its elements into the new array.
# *
# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# */
# static VALUE
# rb_ary_flatten(ary)
# VALUE ary;
# {
# ary = rb_obj_dup(ary);
# rb_ary_flatten_bang(ary);
# return ary;
# }
# /*
# * Returns a new array that is a one-dimensional flattening of this
# * array (recursively). That is, for every element that is an array,
# * extract its elements into the new array.
# *
# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# */
# static VALUE
# rb_ary_flatten(ary)
# VALUE ary;
# {
# ary = rb_obj_dup(ary);
# rb_ary_flatten_bang(ary);
# return ary;
# }
#
# ...
# ...
#
# void
# Init_Array()
# {
# ...
# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
# void
# Init_Array()
# {
# ...
# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
#
# Here RDoc will determine from the rb_define_method line that there's a
# method called "flatten" in class Array, and will look for the implementation
@ -47,9 +47,6 @@ require 'rdoc/known_classes'
# method in the HTML output. This method must be in the same source file
# as the rb_define_method.
#
# C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc
# integrates C and Ruby source into one tree
#
# The comment blocks may include special directives:
#
# [Document-class: <i>name</i>]
@ -68,7 +65,7 @@ require 'rdoc/known_classes'
# Ruby function is in the same source file as the rb_define_method call.
# If this isn't the case, add the comment:
#
# rb_define_method(....); // in filename
# rb_define_method(....); // in: filename
#
# As an example, we might have an extension that defines multiple classes
# in its Init_xxx method. We could document them using
@ -79,7 +76,7 @@ require 'rdoc/known_classes'
# * Encapsulate the writing and reading of the configuration
# * file. ...
# */
#
#
# /*
# * Document-method: read_value
# *
@ -96,8 +93,23 @@ class RDoc::Parser::C < RDoc::Parser
parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
@@enclosure_classes = {}
@@known_bodies = {}
include RDoc::Text
##
# C file the parser is parsing
attr_accessor :content
##
# Resets cross-file state. Call when parsing different projects that need
# separate documentation.
def self.reset
@@enclosure_classes = {}
@@known_bodies = {}
end
reset
##
# Prepare to parse a C file
@ -164,19 +176,17 @@ class RDoc::Parser::C < RDoc::Parser
def do_constants
@content.scan(%r{\Wrb_define_
(
variable |
readonly_variable |
const |
global_const |
)
( variable |
readonly_variable |
const |
global_const | )
\s*\(
(?:\s*(\w+),)?
\s*"(\w+)",
\s*(.*?)\s*\)\s*;
}xm) do |type, var_name, const_name, definition|
var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
handle_constants(type, var_name, const_name, definition)
handle_constants type, var_name, const_name, definition
end
end
@ -270,12 +280,13 @@ class RDoc::Parser::C < RDoc::Parser
def find_body(class_name, meth_name, meth_obj, body, quiet = false)
case body
when %r"((?>/\*.*?\*/\s*))(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
\s*(\([^)]*\))([^;]|$)"xm
comment, params = $1, $2
body_text = $&
when %r"((?>/\*.*?\*/\s*))((?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
\s*(\([^)]*\))([^;]|$))"xm
comment = $1
body_text = $2
params = $3
remove_private_comments(comment) if comment
remove_private_comments comment if comment
# see if we can find the whole body
@ -288,33 +299,40 @@ class RDoc::Parser::C < RDoc::Parser
# distinct (for example Kernel.hash and Kernel.object_id share the same
# implementation
override_comment = find_override_comment(class_name, meth_obj.name)
override_comment = find_override_comment class_name, meth_obj.name
comment = override_comment if override_comment
find_modifiers(comment, meth_obj) if comment
find_modifiers comment, meth_obj if comment
# meth_obj.params = params
meth_obj.start_collecting_tokens
meth_obj.add_token(RDoc::RubyToken::Token.new(1,1).set_text(body_text))
meth_obj.comment = mangle_comment(comment)
when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
tk = RDoc::RubyToken::Token.new nil, 1, 1
tk.set_text body_text
meth_obj.add_token tk
meth_obj.comment = strip_stars comment
when %r{((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))}m
comment = $1
find_body(class_name, $2, meth_obj, body, true)
find_modifiers(comment, meth_obj)
meth_obj.comment = mangle_comment(comment) + meth_obj.comment
body_text = $2
find_body class_name, $3, meth_obj, body, true
find_modifiers comment, meth_obj
meth_obj.start_collecting_tokens
tk = RDoc::RubyToken::Token.new nil, 1, 1
tk.set_text body_text
meth_obj.add_token tk
meth_obj.comment = strip_stars(comment) + meth_obj.comment.to_s
when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
unless find_body(class_name, $1, meth_obj, body, true)
warn "No definition for #{meth_name}" unless @options.quiet
return false
end
else
# No body, but might still have an override comment
comment = find_override_comment(class_name, meth_obj.name)
if comment
find_modifiers(comment, meth_obj)
meth_obj.comment = mangle_comment(comment)
meth_obj.comment = strip_stars comment
else
warn "No definition for #{meth_name}" unless @options.quiet
return false
@ -328,7 +346,7 @@ class RDoc::Parser::C < RDoc::Parser
if raw_name =~ /^rb_m/
container = @top_level.add_module RDoc::NormalModule, name
else
container = @top_level.add_class RDoc::NormalClass, name, nil
container = @top_level.add_class RDoc::NormalClass, name
end
container.record_location @top_level
@ -363,27 +381,23 @@ class RDoc::Parser::C < RDoc::Parser
# */
# VALUE cFoo = rb_define_class("Foo", rb_cObject);
def find_class_comment(class_name, class_meth)
def find_class_comment(class_name, class_mod)
comment = nil
if @content =~ %r{((?>/\*.*?\*/\s+))
(static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi then
if @content =~ %r{
((?>/\*.*?\*/\s+))
(static\s+)?
void\s+
Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)}xmi then # )
comment = $1
elsif @content =~ %r{Document-(?:class|module):\s#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m
elsif @content =~ %r{Document-(?:class|module):\s+#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m then
comment = $1
elsif @content =~ %r{((?>/\*.*?\*/\s+))
([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"}xm then
comment = $1
else
if @content =~ /rb_define_(class|module)/m then
class_name = class_name.split("::").last
comments = []
@content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
comments[index] = chunk
if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
comment = comments[index-1]
break
end
end
end
end
class_meth.comment = mangle_comment(comment) if comment
class_mod.comment = strip_stars comment if comment
end
##
@ -434,67 +448,67 @@ class RDoc::Parser::C < RDoc::Parser
def handle_attr(var_name, attr_name, reader, writer)
rw = ''
if reader
#@stats.num_methods += 1
rw << 'R'
end
if writer
#@stats.num_methods += 1
rw << 'W'
end
rw << 'R' if reader
rw << 'W' if writer
class_name = @known_classes[var_name]
return unless class_name
class_obj = find_class(var_name, class_name)
class_obj = find_class(var_name, class_name)
if class_obj
comment = find_attr_comment(attr_name)
unless comment.empty?
comment = mangle_comment(comment)
end
comment = strip_stars comment
att = RDoc::Attr.new '', attr_name, rw, comment
@stats.add_method att
class_obj.add_attribute(att)
end
end
def handle_class_module(var_name, class_mod, class_name, parent, in_module)
def handle_class_module(var_name, type, class_name, parent, in_module)
parent_name = @known_classes[parent] || parent
if in_module
if in_module then
enclosure = @classes[in_module] || @@enclosure_classes[in_module]
unless enclosure
if enclosure = @known_classes[in_module]
handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
enclosure, nil, nil)
enclosure = @classes[in_module]
end
if enclosure.nil? and enclosure = @known_classes[in_module] then
type = /^rb_m/ =~ in_module ? "module" : "class"
handle_class_module in_module, type, enclosure, nil, nil
enclosure = @classes[in_module]
end
unless enclosure
warn("Enclosing class/module '#{in_module}' for " +
"#{class_mod} #{class_name} not known")
unless enclosure then
warn("Enclosing class/module '#{in_module}' for #{type} #{class_name} not known")
return
end
else
enclosure = @top_level
end
if class_mod == "class" then
full_name = enclosure.full_name.to_s + "::#{class_name}"
if type == "class" then
full_name = if RDoc::ClassModule === enclosure then
enclosure.full_name + "::#{class_name}"
else
class_name
end
if @content =~ %r{Document-class:\s+#{full_name}\s*<\s+([:,\w]+)} then
parent_name = $1
end
cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
@stats.add_class cm
else
cm = enclosure.add_module RDoc::NormalModule, class_name
@stats.add_module cm
end
cm.record_location(enclosure.toplevel)
cm.record_location enclosure.top_level
find_class_comment cm.full_name, cm
find_class_comment(cm.full_name, cm)
@classes[var_name] = cm
@@enclosure_classes[var_name] = cm
@known_classes[var_name] = cm.full_name
@ -512,46 +526,55 @@ class RDoc::Parser::C < RDoc::Parser
# Values may include quotes and escaped colons (\:).
def handle_constants(type, var_name, const_name, definition)
#@stats.num_constants += 1
class_name = @known_classes[var_name]
return unless class_name
class_obj = find_class(var_name, class_name)
class_obj = find_class var_name, class_name
unless class_obj
warn("Enclosing class/module '#{const_name}' for not known")
unless class_obj then
warn "Enclosing class/module #{const_name.inspect} not known"
return
end
comment = find_const_comment(type, const_name)
comment = find_const_comment type, const_name
comment = strip_stars comment
comment = normalize_comment comment
# In the case of rb_define_const, the definition and comment are in
# "/* definition: comment */" form. The literal ':' and '\' characters
# can be escaped with a backslash.
if type.downcase == 'const' then
elements = mangle_comment(comment).split(':')
if elements.nil? or elements.empty? then
con = RDoc::Constant.new(const_name, definition,
mangle_comment(comment))
else
new_definition = elements[0..-2].join(':')
if new_definition.empty? then # Default to literal C definition
new_definition = definition
else
new_definition.gsub!("\:", ":")
new_definition.gsub!("\\", '\\')
end
new_definition.sub!(/\A(\s+)/, '')
new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
con = RDoc::Constant.new(const_name, new_definition,
mangle_comment(new_comment))
end
elements = comment.split ':'
if elements.nil? or elements.empty? then
con = RDoc::Constant.new const_name, definition, comment
else
new_definition = elements[0..-2].join(':')
if new_definition.empty? then # Default to literal C definition
new_definition = definition
else
new_definition.gsub!("\:", ":")
new_definition.gsub!("\\", '\\')
end
new_definition.sub!(/\A(\s+)/, '')
new_comment = if $1.nil? then
elements.last.lstrip
else
"#{$1}#{elements.last.lstrip}"
end
con = RDoc::Constant.new const_name, new_definition, new_comment
end
else
con = RDoc::Constant.new const_name, definition, mangle_comment(comment)
con = RDoc::Constant.new const_name, definition, comment
end
class_obj.add_constant(con)
@stats.add_constant con
class_obj.add_constant con
end
##
@ -588,9 +611,11 @@ class RDoc::Parser::C < RDoc::Parser
meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")"
end
if source_file then
if source_file and File.exist? source_file then
file_name = File.join(@file_dir, source_file)
body = (@@known_bodies[source_file] ||= File.read(file_name))
elsif source_file then
warn "unknown source file #{source_file}"
else
body = @content
end
@ -598,6 +623,7 @@ class RDoc::Parser::C < RDoc::Parser
if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then
class_obj.add_method meth_obj
@stats.add_method meth_obj
meth_obj.visibility = :private if 'private_method' == type
end
end
end
@ -614,16 +640,6 @@ class RDoc::Parser::C < RDoc::Parser
end
end
##
# Remove the /*'s and leading asterisks from C comments
def mangle_comment(comment)
comment.sub!(%r{/\*+}) { " " * $&.length }
comment.sub!(%r{\*+/}) { " " * $&.length }
comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
comment
end
##
# Removes lines that are commented out that might otherwise get picked up
# when scanning for classes and methods
@ -651,11 +667,5 @@ class RDoc::Parser::C < RDoc::Parser
@top_level
end
def warn(msg)
$stderr.puts
$stderr.puts msg
$stderr.flush
end
end

Some files were not shown because too many files have changed in this diff Show more