Import RDoc 2.5
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27147 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
|
|
@ -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.
|
||||
|
|
|
|||
23
bin/rdoc
|
|
@ -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
|
||||
|
||||
|
|
|
|||
100
lib/rdoc.rb
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
232
lib/rdoc/README
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
58
lib/rdoc/constant.rb
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
249
lib/rdoc/dot.rb
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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® 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® 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
|
||||
|
||||
459
lib/rdoc/generator/darkfish.rb
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 %>
|
||||
(<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 %>
|
||||
(<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"] %>
|
||||
<% 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"] %>
|
||||
<% 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> </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>-></td>
|
||||
<td class="context-item-value"><%= aliases["new_name"] %></td>
|
||||
</tr>
|
||||
<% if aliases["desc"] then %>
|
||||
<tr class="top-aligned-row context-row">
|
||||
<td> </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"> [<%= attribute["rw"] %>] </td>
|
||||
<% end
|
||||
unless attribute["rw"] then %>
|
||||
<td class="context-item-value"> </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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"> [<%= attributes["rw"] %>] </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 %>
|
||||
(<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 %>
|
||||
(<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
|
||||
|
|
@ -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> < <%= 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
|
||||
|
||||
171
lib/rdoc/generator/markup.rb
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
297
lib/rdoc/generator/template/darkfish/classpage.rhtml
Normal 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&height=550&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(/->/, '→').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>
|
||||
|
||||
124
lib/rdoc/generator/template/darkfish/filepage.rhtml
Normal 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>
|
||||
|
||||
BIN
lib/rdoc/generator/template/darkfish/images/brick.png
Normal file
|
After Width: | Height: | Size: 452 B |
BIN
lib/rdoc/generator/template/darkfish/images/brick_link.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
lib/rdoc/generator/template/darkfish/images/bug.png
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
lib/rdoc/generator/template/darkfish/images/bullet_black.png
Normal file
|
After Width: | Height: | Size: 211 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 209 B |
BIN
lib/rdoc/generator/template/darkfish/images/date.png
Normal file
|
After Width: | Height: | Size: 626 B |
BIN
lib/rdoc/generator/template/darkfish/images/find.png
Normal file
|
After Width: | Height: | Size: 659 B |
BIN
lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
lib/rdoc/generator/template/darkfish/images/macFFBgHack.png
Normal file
|
After Width: | Height: | Size: 207 B |
BIN
lib/rdoc/generator/template/darkfish/images/package.png
Normal file
|
After Width: | Height: | Size: 853 B |
BIN
lib/rdoc/generator/template/darkfish/images/page_green.png
Normal file
|
After Width: | Height: | Size: 621 B |
BIN
lib/rdoc/generator/template/darkfish/images/page_white_text.png
Normal file
|
After Width: | Height: | Size: 342 B |
BIN
lib/rdoc/generator/template/darkfish/images/page_white_width.png
Normal file
|
After Width: | Height: | Size: 309 B |
BIN
lib/rdoc/generator/template/darkfish/images/plugin.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
lib/rdoc/generator/template/darkfish/images/ruby.png
Normal file
|
After Width: | Height: | Size: 592 B |
BIN
lib/rdoc/generator/template/darkfish/images/tag_green.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
lib/rdoc/generator/template/darkfish/images/wrench.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
lib/rdoc/generator/template/darkfish/images/wrench_orange.png
Normal file
|
After Width: | Height: | Size: 584 B |
BIN
lib/rdoc/generator/template/darkfish/images/zoom.png
Normal file
|
After Width: | Height: | Size: 692 B |
64
lib/rdoc/generator/template/darkfish/index.rhtml
Normal 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 %> — <%= 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>
|
||||
116
lib/rdoc/generator/template/darkfish/js/darkfish.js
Normal 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 );
|
||||
});
|
||||
32
lib/rdoc/generator/template/darkfish/js/jquery.js
vendored
Normal file
114
lib/rdoc/generator/template/darkfish/js/quicksearch.js
Normal 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();
|
||||
};
|
||||
701
lib/rdoc/generator/template/darkfish/rdoc.css
Normal 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 */
|
||||
|
|
@ -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
|
||||
|
|
@ -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 %>
|
||||
|
|
@ -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 %>
|
||||
|
|
@ -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"] %>
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
19
lib/rdoc/markup/blank_line.rb
Normal 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
|
||||
|
||||
72
lib/rdoc/markup/document.rb
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
341
lib/rdoc/markup/formatter_test_case.rb
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
17
lib/rdoc/markup/heading.rb
Normal 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
|
||||
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
83
lib/rdoc/markup/list_item.rb
Normal 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
|
||||
|
||||
66
lib/rdoc/markup/paragraph.rb
Normal 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
|
|
@ -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'
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
72
lib/rdoc/markup/to_ansi.rb
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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\)/, '®')
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
42
lib/rdoc/markup/verbatim.rb
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||