mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add RDoc
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5073 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
c1c55573bd
commit
87762adcb0
36 changed files with 12367 additions and 0 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
Mon Dec 1 16:10:52 2003 Dave Thomas <dave@pragprog.com>
|
||||||
|
|
||||||
|
* lib/rdoc/rdoc.rb: (etc) initial merge into main tree.
|
||||||
|
|
||||||
Mon Dec 1 14:17:49 2003 Minero Aoki <aamine@loveruby.net>
|
Mon Dec 1 14:17:49 2003 Minero Aoki <aamine@loveruby.net>
|
||||||
|
|
||||||
* lib/fileutils.rb (fu_each_src_dest0): call #to_str to allow
|
* lib/fileutils.rb (fu_each_src_dest0): call #to_str to allow
|
||||||
|
|
67
bin/rdoc
Normal file
67
bin/rdoc
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
#
|
||||||
|
# RDoc: Documentation tool for source code
|
||||||
|
# (see lib/rdoc/rdoc.rb for more information)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2003 Dave Thomas
|
||||||
|
# Released under the same terms as Ruby
|
||||||
|
#
|
||||||
|
# $Revision$
|
||||||
|
|
||||||
|
## Transitional Hack ####
|
||||||
|
#
|
||||||
|
# RDoc was initially distributed independently, and installed
|
||||||
|
# itself into <prefix>/lib/ruby/site_ruby/<ver>/rdoc...
|
||||||
|
#
|
||||||
|
# Now that RDoc is part of the distribution, it's installed into
|
||||||
|
# <prefix>/lib/ruby/<ver>, which unfortunately appears later in the
|
||||||
|
# search path. This means that if you have previously installed RDoc,
|
||||||
|
# and then install from ruby-lang, you'll pick up the old one by
|
||||||
|
# default. This hack checks for the condition, and readjusts the
|
||||||
|
# search path if necessary.
|
||||||
|
|
||||||
|
def adjust_for_existing_rdoc(path)
|
||||||
|
|
||||||
|
$stderr.puts %{
|
||||||
|
It seems as if you have a previously-installed RDoc in
|
||||||
|
the directory #{path}.
|
||||||
|
|
||||||
|
Because this is now out-of-date, you might want to consider
|
||||||
|
removing the directories:
|
||||||
|
|
||||||
|
#{File.join(path, "rdoc")}
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
#{File.join(path, "markup")}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move all the site_ruby directories to the end
|
||||||
|
p $:
|
||||||
|
$:.replace($:.partition {|path| /site_ruby/ !~ path}.flatten)
|
||||||
|
p $:
|
||||||
|
end
|
||||||
|
|
||||||
|
$:.each do |path|
|
||||||
|
if /site_ruby/ =~ path
|
||||||
|
rdoc_path = File.join(path, 'rdoc', 'rdoc.rb')
|
||||||
|
if File.exists?(rdoc_path)
|
||||||
|
adjust_for_existing_rdoc(path)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
## End of Transitional Hack ##
|
||||||
|
|
||||||
|
|
||||||
|
require 'rdoc/rdoc'
|
||||||
|
|
||||||
|
begin
|
||||||
|
r = RDoc::RDoc.new
|
||||||
|
r.document(ARGV)
|
||||||
|
rescue RDoc::RDocError => e
|
||||||
|
$stderr.puts e.message
|
||||||
|
exit(1)
|
||||||
|
end
|
445
lib/rdoc/README
Normal file
445
lib/rdoc/README
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
= RDOC - Ruby Documentation System
|
||||||
|
|
||||||
|
This package contains Rdoc and SimpleMarkup. 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. (Currently, HTML is the only
|
||||||
|
supported format. 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.
|
||||||
|
|
||||||
|
This library contains two packages, rdoc itself and a text markup
|
||||||
|
library, 'markup'.
|
||||||
|
|
||||||
|
== 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/parsers/parse_c.rb.
|
||||||
|
* For information on the various markups available in comment
|
||||||
|
blocks, see markup/simple_markup.rb.
|
||||||
|
* If you want to drive Rdoc programatically, see RDoc::RDoc.
|
||||||
|
* If you want to use the library to format text blocks into HTML,
|
||||||
|
have a look at SM::SimpleMarkup.
|
||||||
|
* If you want to try writing your own HTML output template, see
|
||||||
|
RDoc::Page.
|
||||||
|
|
||||||
|
== 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/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 <tt>.rb</tt> and <tt>.rbw</tt> are assumed to be Ruby
|
||||||
|
source. Files ending <tt>.c</tt> are parsed as C files. All other
|
||||||
|
files are assumed to contain just SimpleMarkup-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.
|
||||||
|
|
||||||
|
== 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.
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
= Usage
|
||||||
|
|
||||||
|
RDoc is invoked from the command line using:
|
||||||
|
|
||||||
|
% rdoc <options> [name...]
|
||||||
|
|
||||||
|
Files are parsed, and the information they contain collected, before
|
||||||
|
any output is produced. This allows cross references between all files
|
||||||
|
to be resolved. If a name is a directory, it is traversed. If no
|
||||||
|
names are specified, all Ruby files in the current directory (and
|
||||||
|
subdirectories) are processed.
|
||||||
|
|
||||||
|
Options are:
|
||||||
|
|
||||||
|
[<tt>--accessor</tt> <i>name[,name...]</i>]
|
||||||
|
specifies the name(s) of additional methods that should be treated
|
||||||
|
as if they were <tt>attr_</tt><i>xxx</i> methods. Specifying
|
||||||
|
"--accessor db_opt" means lines such as
|
||||||
|
|
||||||
|
db_opt :name, :age
|
||||||
|
|
||||||
|
will get parsed and displayed in the documentation. Each name may have an
|
||||||
|
optional "=flagtext" appended, in which case the given flagtext will appear
|
||||||
|
where (for example) the 'rw' appears for attr_accessor.
|
||||||
|
|
||||||
|
[<tt>--all</tt>]
|
||||||
|
include protected and private methods in the output (by default
|
||||||
|
only public methods are included)
|
||||||
|
|
||||||
|
[<tt>--charset</tt> _charset_]
|
||||||
|
Set the character set for the generated HTML.
|
||||||
|
|
||||||
|
[<tt>--diagram</tt>]
|
||||||
|
include diagrams showing modules and classes. This is currently
|
||||||
|
an experimental feature, and may not be supported by all output
|
||||||
|
templates. You need dot V1.8.6 or later to use the --diagram
|
||||||
|
option correctly (http://www.research.att.com/sw/tools/graphviz/).
|
||||||
|
|
||||||
|
[<tt>--exclude</tt> <i>pattern</i>]
|
||||||
|
exclude files and directories matching this pattern from processing
|
||||||
|
|
||||||
|
[<tt>--extension</tt> <i>new=old</i>]
|
||||||
|
treat files ending <i>.new</i> as if they ended
|
||||||
|
<i>.old</i>. Saying '--extension cgi=rb' causes RDoc to treat .cgi
|
||||||
|
files as Ruby source.
|
||||||
|
|
||||||
|
[<tt>fileboxes</tt>]
|
||||||
|
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 sharing them. Silently discarded if
|
||||||
|
--diagram is not given Experimental.
|
||||||
|
|
||||||
|
[<tt>--fmt</tt> _fmt_]
|
||||||
|
generate output in a particular format.
|
||||||
|
|
||||||
|
[<tt>--help</tt>]
|
||||||
|
generate a usage summary.
|
||||||
|
|
||||||
|
[<tt>--help-output</tt>]
|
||||||
|
explain the various output options.
|
||||||
|
|
||||||
|
[<tt>--image-format</tt> <i>gif/png/jpg/jpeg</i>]
|
||||||
|
sets output image format for diagrams. Can be png, gif, jpeg,
|
||||||
|
jpg. If this option is omitted, png is used. Requires --diagram.
|
||||||
|
|
||||||
|
[<tt>--include</tt> <i>dir,...</i>]
|
||||||
|
specify one or more directories to be searched when satisfying
|
||||||
|
:+include+: directives. Multiple <tt>--include</tt> options may be
|
||||||
|
given. The directory containing the file currently being processed
|
||||||
|
is always searched.
|
||||||
|
|
||||||
|
[<tt>--inline-source</tt>]
|
||||||
|
By default, the source code of methods is shown in a popup. With
|
||||||
|
this option, it's displayed inline.
|
||||||
|
|
||||||
|
[<tt>line-numbers</tt>]
|
||||||
|
include line numbers in the source code
|
||||||
|
|
||||||
|
[<tt>--main</tt> _name_]
|
||||||
|
set the class, module, or file to appear on the index page
|
||||||
|
|
||||||
|
[<tt>--one-file</tt>]
|
||||||
|
place all the output into a single file
|
||||||
|
|
||||||
|
[<tt>--op</tt> _dir_]
|
||||||
|
set the output directory to _dir_ (the default is the directory
|
||||||
|
"doc")
|
||||||
|
|
||||||
|
[<tt>--op-name</tt> _name_]
|
||||||
|
set the name of the output. Has no effect for HTML.
|
||||||
|
"doc")
|
||||||
|
|
||||||
|
[<tt>--opname</tt> _name_]
|
||||||
|
set the output name (has no effect for HTML).
|
||||||
|
|
||||||
|
[<tt>--promiscuous</tt>]
|
||||||
|
If a module or class is defined in more than one source file, and
|
||||||
|
you click on a particular file's name in the top navigation pane,
|
||||||
|
RDoc will normally only show you the inner classes and modules of
|
||||||
|
that class that are defined in the particular file. Using this
|
||||||
|
option makes it show all classes and modules defined in the class,
|
||||||
|
regardless of the file they were defined in.
|
||||||
|
|
||||||
|
[<tt>--quiet</tt>]
|
||||||
|
do not display progress messages
|
||||||
|
|
||||||
|
[<tt>--show-hash</tt>]
|
||||||
|
A name of the form #name in a comment is a possible hyperlink to
|
||||||
|
an instance method name. When displayed, the '#' is removed unless
|
||||||
|
this option is specified
|
||||||
|
|
||||||
|
[<tt>--style</tt> <i>stylesheet url</i>]
|
||||||
|
specifies the URL of an external stylesheet to use (rather than
|
||||||
|
generating one of our own)
|
||||||
|
|
||||||
|
[<tt>tab-width</tt> _n_]
|
||||||
|
set the width of tab characters (default 8)
|
||||||
|
|
||||||
|
[<tt>--template</tt> <i>name</i>]
|
||||||
|
specify an alternate template to use when generating output (the
|
||||||
|
default is 'standard'). This template should be in a directory
|
||||||
|
accessible via $: as rdoc/generators/xxxx_template, where 'xxxx'
|
||||||
|
depends on the output formatter.
|
||||||
|
|
||||||
|
[<tt>--version</tt>]
|
||||||
|
display RDoc's version
|
||||||
|
|
||||||
|
[<tt>--webcvs</tt> _url_]
|
||||||
|
Specify a URL for linking to a web frontend to CVS. If the URL
|
||||||
|
contains a '\%s', the name of the current file will be
|
||||||
|
substituted; if the URL doesn't contain a '\%s', the filename will
|
||||||
|
be appended to it.
|
||||||
|
|
||||||
|
= Example
|
||||||
|
|
||||||
|
A typical small Ruby program commented using RDoc might be as follows. You
|
||||||
|
can see the formatted result in EXAMPLE.rb and Anagram.
|
||||||
|
|
||||||
|
:include: EXAMPLE.rb
|
||||||
|
|
||||||
|
= Markup
|
||||||
|
|
||||||
|
Comment blocks can be written fairly naturally.
|
||||||
|
|
||||||
|
Paragraphs are lines that share the left margin. Text indented past
|
||||||
|
this margin are formatted verbatim.
|
||||||
|
|
||||||
|
1. Lists are typed as indented paragraphs with:
|
||||||
|
* a '*' or '-' (for bullet lists)
|
||||||
|
* a digit followed by a period for
|
||||||
|
numbered lists
|
||||||
|
* an upper or lower case letter followed
|
||||||
|
by a period for alpha lists.
|
||||||
|
|
||||||
|
For example, the input that produced the above paragraph looked like
|
||||||
|
1. Lists are typed as indented
|
||||||
|
paragraphs with:
|
||||||
|
* a '*' or '-' (for bullet lists)
|
||||||
|
* a digit followed by a period for
|
||||||
|
numbered lists
|
||||||
|
* an upper or lower case letter followed
|
||||||
|
by a period for alpha lists.
|
||||||
|
|
||||||
|
2. Labeled lists (sometimes called description
|
||||||
|
lists) are typed using square brackets for the label.
|
||||||
|
[cat] small domestic animal
|
||||||
|
[+cat+] command to copy standard input
|
||||||
|
|
||||||
|
3. Labeled lists may also be produced by putting a double colon
|
||||||
|
after the label. This sets the result in tabular form, so the
|
||||||
|
descriptions all line up. This was used to create the 'author'
|
||||||
|
block at the bottom of this description.
|
||||||
|
cat:: small domestic animal
|
||||||
|
+cat+:: command to copy standard input
|
||||||
|
|
||||||
|
For both kinds of labeled lists, if the body text starts on the same
|
||||||
|
line as the label, then the start of that text determines the block
|
||||||
|
indent for the rest of the body. The text may also start on the line
|
||||||
|
following the label, indented from the start of the label. This is
|
||||||
|
often preferable if the label is long. Both the following are
|
||||||
|
valid labeled list entries:
|
||||||
|
|
||||||
|
<tt>--output</tt> <i>name [, name]</i>::
|
||||||
|
specify the name of one or more output files. If multiple
|
||||||
|
files are present, the first is used as the index.
|
||||||
|
|
||||||
|
<tt>--quiet:</tt>:: do not output the names, sizes, byte counts,
|
||||||
|
index areas, or bit ratios of units as
|
||||||
|
they are processed.
|
||||||
|
|
||||||
|
4. Headings are entered using equals signs
|
||||||
|
|
||||||
|
= Level One Heading
|
||||||
|
== Level Two Heading
|
||||||
|
and so on
|
||||||
|
|
||||||
|
5. Rules (horizontal lines) are entered using three or
|
||||||
|
more hyphens.
|
||||||
|
|
||||||
|
6. Non-verbatim text can be marked up:
|
||||||
|
|
||||||
|
_italic_:: \_word_ or \<em>text</em>
|
||||||
|
*bold*:: \*word* or \<b>text</b>
|
||||||
|
+typewriter+:: \+word+ or \<tt>text</tt>
|
||||||
|
|
||||||
|
The first form only works around 'words', where a word is a
|
||||||
|
sequence of upper and lower case letters and underscores. Putting a
|
||||||
|
backslash before inline markup stops it being interpreted, which is
|
||||||
|
how I created the table above:
|
||||||
|
|
||||||
|
_italic_:: \_word_ or \<em>text</em>
|
||||||
|
*bold*:: \*word* or \<b>text</b>
|
||||||
|
+typewriter+:: \+word+ or \<tt>text</tt>
|
||||||
|
|
||||||
|
7. 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.
|
||||||
|
|
||||||
|
8. 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
|
||||||
|
assumed to refer to local files whose path is relative to the --op
|
||||||
|
directory.
|
||||||
|
|
||||||
|
Hyperlinks can also be of the form <tt>label</tt>[url], in which
|
||||||
|
case the label is used in the displayed text, and <tt>url</tt> is
|
||||||
|
used as the target.
|
||||||
|
|
||||||
|
9. 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| ... }
|
||||||
|
|
||||||
|
|
||||||
|
10. ':yields:' is an example of a documentation modifier. These appear
|
||||||
|
immediately after the start of the document element they are modifying.
|
||||||
|
Other modifiers include
|
||||||
|
|
||||||
|
[<tt>:nodoc:</tt><i>[all]</i>]
|
||||||
|
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 SM #:nodoc:
|
||||||
|
class Input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module Markup #:nodoc: all
|
||||||
|
class Output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
In the above code, only class <tt>SM::Input</tt> will be
|
||||||
|
documented.
|
||||||
|
|
||||||
|
[<tt>:doc:</tt>]
|
||||||
|
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.
|
||||||
|
|
||||||
|
[<tt>:notnew:</tt>]
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
11. RDoc stops processing comments if it finds a comment
|
||||||
|
line containing '<tt>#--</tt>'. This can be used to
|
||||||
|
separate external from internal comments, or
|
||||||
|
to stop a comment being associated with a method,
|
||||||
|
class, or module. Commenting can be turned back on with
|
||||||
|
a line that starts '<tt>#++</tt>'.
|
||||||
|
|
||||||
|
# Extract the age and calculate the
|
||||||
|
# date-of-birth.
|
||||||
|
#--
|
||||||
|
# FIXME: fails if the birthday falls on
|
||||||
|
# February 29th
|
||||||
|
#++
|
||||||
|
# The DOB is returned as a Time object.
|
||||||
|
|
||||||
|
def get_dob(person)
|
||||||
|
...
|
||||||
|
|
||||||
|
12. Comment blocks can contain other directives:
|
||||||
|
|
||||||
|
[<tt>:include:</tt><i>filename</i>]
|
||||||
|
include the contents of the named file at this point. The
|
||||||
|
file will be searched for in the directories listed by
|
||||||
|
the <tt>--include</tt> 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.
|
||||||
|
|
||||||
|
[<tt>:title:</tt><i>text</i>]
|
||||||
|
Sets the title for the document. Equivalent to the --title command
|
||||||
|
line parameter. (The command line parameter overrides any :title:
|
||||||
|
directive in the source).
|
||||||
|
|
||||||
|
[<tt>:enddoc:</tt>]
|
||||||
|
Document nothing further at the current level.
|
||||||
|
|
||||||
|
[<tt>:main:</tt><i>name</i>]
|
||||||
|
Equivalent to the --main command line parameter.
|
||||||
|
|
||||||
|
[<tt>:stopdoc: / :startdoc:</tt>]
|
||||||
|
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
|
||||||
|
<tt>:stopdoc:</tt> before the first, and a
|
||||||
|
<tt>:startdoc:</tt> after the last. If you don't specifiy a
|
||||||
|
<tt>:startdoc:</tt> by the end of the container, disables
|
||||||
|
documentation for the entire class or module.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
See also markup/simple_markup.rb.
|
||||||
|
|
||||||
|
= Other stuff
|
||||||
|
|
||||||
|
Author:: Dave Thomas <dave@pragmaticprogrammer.com>
|
||||||
|
Requires:: Ruby 1.8.1 or later
|
||||||
|
License:: Copyright (c) 2001-2003 Dave Thomas.
|
||||||
|
Released under the same license as Ruby.
|
||||||
|
|
||||||
|
== 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.
|
653
lib/rdoc/code_objects.rb
Normal file
653
lib/rdoc/code_objects.rb
Normal file
|
@ -0,0 +1,653 @@
|
||||||
|
# We represent the various high-level code constructs that appear
|
||||||
|
# in Ruby programs: classes, modules, methods, and so on.
|
||||||
|
|
||||||
|
require 'rdoc/tokenstream'
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
|
||||||
|
# We contain the common stuff for contexts (which are containers)
|
||||||
|
# and other elements (methods, attributes and so on)
|
||||||
|
#
|
||||||
|
class CodeObject
|
||||||
|
|
||||||
|
attr_accessor :parent
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# are we done documenting (ie, did we come across a :enddoc:)?
|
||||||
|
|
||||||
|
attr_accessor :done_documenting
|
||||||
|
|
||||||
|
|
||||||
|
# do we document ourselves?
|
||||||
|
|
||||||
|
attr_reader :document_self
|
||||||
|
|
||||||
|
def document_self=(val)
|
||||||
|
@document_self = val
|
||||||
|
if !val
|
||||||
|
remove_methods_etc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# set and cleared by :startdoc: and :enddoc:, this is used to toggle
|
||||||
|
# the capturing of documentation
|
||||||
|
def start_doc
|
||||||
|
@document_self = true
|
||||||
|
@document_children = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop_doc
|
||||||
|
@document_self = false
|
||||||
|
@document_children = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# do we document ourselves and our children
|
||||||
|
|
||||||
|
attr_reader :document_children
|
||||||
|
|
||||||
|
def document_children=(val)
|
||||||
|
@document_children = val
|
||||||
|
if !val
|
||||||
|
remove_classes_and_modules
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Do we _force_ documentation, even is we wouldn't normally show the entity
|
||||||
|
attr_accessor :force_documentation
|
||||||
|
|
||||||
|
# Default callbacks to nothing, but this is overridden for classes
|
||||||
|
# and modules
|
||||||
|
def remove_classes_and_modules
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_methods_etc
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@document_self = true
|
||||||
|
@document_children = true
|
||||||
|
@force_documentation = false
|
||||||
|
@done_documenting = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Access the code object's comment
|
||||||
|
attr_reader :comment
|
||||||
|
|
||||||
|
# Update the comment, but don't overwrite a real comment
|
||||||
|
# with an empty one
|
||||||
|
def comment=(comment)
|
||||||
|
@comment = comment unless comment.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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. Wehn a comment is assigned, we then extract
|
||||||
|
# out any matching directives and update our object
|
||||||
|
|
||||||
|
def CodeObject.attr_overridable(name, *aliases)
|
||||||
|
@overridables ||= {}
|
||||||
|
|
||||||
|
attr_accessor name
|
||||||
|
|
||||||
|
aliases.unshift name
|
||||||
|
aliases.each do |directive_name|
|
||||||
|
@overridables[directive_name.to_s] = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# A Context is something that can hold modules, classes, methods,
|
||||||
|
# attributes, aliases, requires, and includes. Classes, modules, and
|
||||||
|
# files are all Contexts.
|
||||||
|
|
||||||
|
class Context < CodeObject
|
||||||
|
attr_reader :name, :method_list, :attributes, :aliases, :constants
|
||||||
|
attr_reader :requires, :includes, :in_files, :visibility
|
||||||
|
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super()
|
||||||
|
|
||||||
|
@in_files = []
|
||||||
|
|
||||||
|
@name ||= "unknown"
|
||||||
|
@comment ||= ""
|
||||||
|
@parent = nil
|
||||||
|
@visibility = :public
|
||||||
|
|
||||||
|
initialize_methods_etc
|
||||||
|
initialize_classes_and_modules
|
||||||
|
end
|
||||||
|
|
||||||
|
# map the class hash to an array externally
|
||||||
|
def classes
|
||||||
|
@classes.values
|
||||||
|
end
|
||||||
|
|
||||||
|
# map the module hash to an array externally
|
||||||
|
def modules
|
||||||
|
@modules.values
|
||||||
|
end
|
||||||
|
|
||||||
|
# Change the default visibility for new methods
|
||||||
|
def ongoing_visibility=(vis)
|
||||||
|
@visibility = vis
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an array +methods+ of method names, set the
|
||||||
|
# visibility of the corresponding AnyMethod object
|
||||||
|
|
||||||
|
def set_visibility_for(methods, vis, singleton=false)
|
||||||
|
@method_list.each_with_index do |m,i|
|
||||||
|
if methods.include?(m.name) && m.singleton == singleton
|
||||||
|
m.visibility = vis
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Record the file that we happen to find it in
|
||||||
|
def record_location(toplevel)
|
||||||
|
@in_files << toplevel unless @in_files.include?(toplevel)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return true if at least part of this thing was defined in +file+
|
||||||
|
def defined_in?(file)
|
||||||
|
@in_files.include?(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_class(class_type, name, superclass)
|
||||||
|
add_class_or_module(@classes, class_type, name, superclass)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_module(class_type, name)
|
||||||
|
add_class_or_module(@modules, class_type, name, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_method(a_method)
|
||||||
|
puts "Adding #@visibility method #{a_method.name} to #@name" if $DEBUG
|
||||||
|
a_method.visibility = @visibility
|
||||||
|
add_to(@method_list, a_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_attribute(an_attribute)
|
||||||
|
add_to(@attributes, an_attribute)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_alias(an_alias)
|
||||||
|
meth = find_method_named(an_alias.old_name)
|
||||||
|
if meth
|
||||||
|
new_meth = 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 = "Alias for \##{meth.name}"
|
||||||
|
meth.add_alias(new_meth)
|
||||||
|
add_method(new_meth)
|
||||||
|
else
|
||||||
|
add_to(@aliases, an_alias)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_include(an_include)
|
||||||
|
add_to(@includes, an_include)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_constant(const)
|
||||||
|
add_to(@constants, const)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Requires always get added to the top-level (file) context
|
||||||
|
def add_require(a_require)
|
||||||
|
if self.kind_of? TopLevel
|
||||||
|
add_to(@requires, a_require)
|
||||||
|
else
|
||||||
|
parent.add_require(a_require)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_class_or_module(collection, class_type, name, superclass=nil)
|
||||||
|
cls = collection[name]
|
||||||
|
if cls
|
||||||
|
puts "Reusing class/module #{name}" if $DEBUG
|
||||||
|
else
|
||||||
|
cls = class_type.new(name, superclass)
|
||||||
|
puts "Adding class/module #{name} to #@name" if $DEBUG
|
||||||
|
collection[name] = cls
|
||||||
|
cls.parent = self
|
||||||
|
end
|
||||||
|
cls
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_to(array, thing)
|
||||||
|
array << thing if @document_self
|
||||||
|
thing.parent = self
|
||||||
|
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
|
||||||
|
|
||||||
|
def initialize_methods_etc
|
||||||
|
@method_list = []
|
||||||
|
@attributes = []
|
||||||
|
@aliases = []
|
||||||
|
@requires = []
|
||||||
|
@includes = []
|
||||||
|
@constants = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# and remove classes and modules when we see a :nodoc: all
|
||||||
|
def remove_classes_and_modules
|
||||||
|
initialize_classes_and_modules
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_classes_and_modules
|
||||||
|
@classes = {}
|
||||||
|
@modules = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a named module
|
||||||
|
def find_module_named(name)
|
||||||
|
return self if self.name == name
|
||||||
|
res = @modules[name] || @classes[name]
|
||||||
|
return res if res
|
||||||
|
parent && parent.find_module_named(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterate over all the classes and modules in
|
||||||
|
# this object
|
||||||
|
|
||||||
|
def each_classmodule
|
||||||
|
@modules.each_value {|m| yield m}
|
||||||
|
@classes.each_value {|c| yield c}
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_method
|
||||||
|
@method_list.each {|m| yield m}
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_attribute
|
||||||
|
@attributes.each {|a| yield a}
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_constant
|
||||||
|
@constants.each {|c| yield c}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the toplevel that owns us
|
||||||
|
|
||||||
|
def toplevel
|
||||||
|
return @toplevel if defined? @toplevel
|
||||||
|
@toplevel = self
|
||||||
|
@toplevel = @toplevel.parent until TopLevel === @toplevel
|
||||||
|
@toplevel
|
||||||
|
end
|
||||||
|
|
||||||
|
# allow us to sort modules by name
|
||||||
|
def <=>(other)
|
||||||
|
name <=> other.name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look up the given 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 /^::(.*)/
|
||||||
|
result = toplevel.find_symbol(symbol)
|
||||||
|
when /::/
|
||||||
|
modules = symbol.split(/::/)
|
||||||
|
unless modules.empty?
|
||||||
|
module_name = modules.shift
|
||||||
|
result = find_module_named(module_name)
|
||||||
|
if result
|
||||||
|
modules.each do |module_name|
|
||||||
|
result = result.find_module_named(module_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = find_local_symbol(symbol)
|
||||||
|
if result.nil?
|
||||||
|
if symbol =~ /^[A-Z]/
|
||||||
|
result = parent
|
||||||
|
while result && result.name != symbol
|
||||||
|
result = result.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if result && method
|
||||||
|
result = result.find_local_symbol(method)
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_local_symbol(symbol)
|
||||||
|
res = find_method_named(symbol) ||
|
||||||
|
find_constant_named(symbol) ||
|
||||||
|
find_attribute_named(symbol) ||
|
||||||
|
find_module_named(symbol)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Find a named method, or return nil
|
||||||
|
def find_method_named(name)
|
||||||
|
@method_list.find {|meth| meth.name == name}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a named constant, or return nil
|
||||||
|
def find_constant_named(name)
|
||||||
|
@constants.find {|m| m.name == name}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a named attribute, or return nil
|
||||||
|
def find_attribute_named(name)
|
||||||
|
@attributes.find {|m| m.name == name}
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# A TopLevel context is a source file
|
||||||
|
|
||||||
|
class TopLevel < Context
|
||||||
|
attr_accessor :file_stat
|
||||||
|
attr_accessor :file_relative_name
|
||||||
|
attr_accessor :file_absolute_name
|
||||||
|
attr_accessor :diagram
|
||||||
|
|
||||||
|
@@all_classes = {}
|
||||||
|
@@all_modules = {}
|
||||||
|
|
||||||
|
def TopLevel::reset
|
||||||
|
@@all_classes = {}
|
||||||
|
@@all_modules = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(file_name)
|
||||||
|
super()
|
||||||
|
@name = "TopLevel"
|
||||||
|
@file_relative_name = file_name
|
||||||
|
@file_absolute_name = file_name
|
||||||
|
@file_stat = File.stat(file_name)
|
||||||
|
@diagram = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adding a class or module to a TopLevel is special, as we only
|
||||||
|
# want one copy of a particular top-level class. For example,
|
||||||
|
# if both file A and file B implement class C, we only want one
|
||||||
|
# ClassModule object for C. This code arranges to share
|
||||||
|
# classes and modules between files.
|
||||||
|
|
||||||
|
def add_class_or_module(collection, class_type, name, superclass)
|
||||||
|
cls = collection[name]
|
||||||
|
if cls
|
||||||
|
puts "Reusing class/module #{name}" if $DEBUG
|
||||||
|
else
|
||||||
|
if class_type == NormalModule
|
||||||
|
all = @@all_modules
|
||||||
|
else
|
||||||
|
all = @@all_classes
|
||||||
|
end
|
||||||
|
cls = all[name]
|
||||||
|
if !cls
|
||||||
|
cls = class_type.new(name, superclass)
|
||||||
|
all[name] = cls
|
||||||
|
end
|
||||||
|
puts "Adding class/module #{name} to #@name" if $DEBUG
|
||||||
|
collection[name] = cls
|
||||||
|
cls.parent = self
|
||||||
|
end
|
||||||
|
cls
|
||||||
|
end
|
||||||
|
|
||||||
|
def TopLevel.all_classes_and_modules
|
||||||
|
@@all_classes.values + @@all_modules.values
|
||||||
|
end
|
||||||
|
|
||||||
|
def TopLevel.find_class_named(name)
|
||||||
|
@@all_classes.each_value do |c|
|
||||||
|
res = c.find_class_named(name)
|
||||||
|
return res if res
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_local_symbol(symbol)
|
||||||
|
find_class_or_module_named(symbol) || super
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_class_or_module_named(symbol)
|
||||||
|
@@all_classes.each_value {|c| return c if c.name == symbol}
|
||||||
|
@@all_modules.each_value {|m| return m if m.name == symbol}
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# ClassModule is the base class for objects representing either a
|
||||||
|
# class or a module.
|
||||||
|
|
||||||
|
class ClassModule < Context
|
||||||
|
|
||||||
|
attr_reader :superclass
|
||||||
|
attr_accessor :diagram
|
||||||
|
|
||||||
|
def initialize(name, superclass = nil)
|
||||||
|
@name = name
|
||||||
|
@diagram = nil
|
||||||
|
@superclass = superclass
|
||||||
|
@comment = ""
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the fully qualified name of this class or module
|
||||||
|
def full_name
|
||||||
|
if @parent && @parent.full_name
|
||||||
|
@parent.full_name + "::" + @name
|
||||||
|
else
|
||||||
|
@name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def http_url(prefix)
|
||||||
|
path = full_name.split("::")
|
||||||
|
File.join(prefix, *path) + ".html"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return +true+ if this object represents a module
|
||||||
|
def is_module?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# to_s is simply for debugging
|
||||||
|
def to_s
|
||||||
|
res = self.class.name + ": " + @name
|
||||||
|
res << @comment.to_s
|
||||||
|
res << super
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_class_named(name)
|
||||||
|
return self if full_name == name
|
||||||
|
@classes.each_value {|c| return c if c.find_class_named(name) }
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Anonymous classes
|
||||||
|
class AnonClass < ClassModule
|
||||||
|
end
|
||||||
|
|
||||||
|
# Normal classes
|
||||||
|
class NormalClass < ClassModule
|
||||||
|
end
|
||||||
|
|
||||||
|
# Singleton classes
|
||||||
|
class SingleClass < ClassModule
|
||||||
|
end
|
||||||
|
|
||||||
|
# Module
|
||||||
|
class NormalModule < ClassModule
|
||||||
|
def is_module?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# AnyMethod is the base class for objects representing methods
|
||||||
|
|
||||||
|
class AnyMethod < CodeObject
|
||||||
|
attr_accessor :name
|
||||||
|
attr_accessor :visibility
|
||||||
|
attr_accessor :block_params
|
||||||
|
attr_accessor :dont_rename_initialize
|
||||||
|
attr_accessor :singleton
|
||||||
|
attr_reader :aliases # list of other names for this method
|
||||||
|
attr_accessor :is_alias_for # or a method we're aliasing
|
||||||
|
|
||||||
|
attr_overridable :params, :param, :parameters, :parameter
|
||||||
|
|
||||||
|
include TokenStream
|
||||||
|
|
||||||
|
def initialize(text, name)
|
||||||
|
super()
|
||||||
|
@text = text
|
||||||
|
@name = name
|
||||||
|
@token_stream = nil
|
||||||
|
@visibility = :public
|
||||||
|
@dont_rename_initialize = false
|
||||||
|
@block_params = nil
|
||||||
|
@aliases = []
|
||||||
|
@is_alias_for = nil
|
||||||
|
@comment = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
@name <=> other.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
res = self.class.name + ": " + @name + " (" + @text + ")\n"
|
||||||
|
res << @comment.to_s
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def param_seq
|
||||||
|
p = params.gsub(/\s*\#.*/, '')
|
||||||
|
p = p.tr("\n", " ").squeeze(" ")
|
||||||
|
p = "(" + p + ")" unless p[0] == ?(
|
||||||
|
|
||||||
|
if (block = block_params)
|
||||||
|
block.gsub!(/\s*\#.*/, '')
|
||||||
|
block = block.tr("\n", " ").squeeze(" ")
|
||||||
|
if block[0] == ?(
|
||||||
|
block.sub!(/^\(/, '').sub!(/\)/, '')
|
||||||
|
end
|
||||||
|
p << " {|#{block}| ...}"
|
||||||
|
end
|
||||||
|
p
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_alias(method)
|
||||||
|
@aliases << method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Represent an alias, which is an old_name/ new_name pair associated
|
||||||
|
# with a particular context
|
||||||
|
class Alias < CodeObject
|
||||||
|
attr_accessor :text, :old_name, :new_name, :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 to_s
|
||||||
|
"alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represent a constant
|
||||||
|
class Constant < CodeObject
|
||||||
|
attr_accessor :name, :value
|
||||||
|
|
||||||
|
def initialize(name, value, comment)
|
||||||
|
super()
|
||||||
|
@name = name
|
||||||
|
@value = value
|
||||||
|
self.comment = comment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represent attributes
|
||||||
|
class Attr < CodeObject
|
||||||
|
attr_accessor :text, :name, :rw
|
||||||
|
|
||||||
|
def initialize(text, name, rw, comment)
|
||||||
|
super()
|
||||||
|
@text = text
|
||||||
|
@name = name
|
||||||
|
@rw = rw
|
||||||
|
self.comment = comment
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"attr: #{self.name} #{self.rw}\n#{self.comment}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
self.name <=> other.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# a required file
|
||||||
|
|
||||||
|
class Require < CodeObject
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
def initialize(name, comment)
|
||||||
|
super()
|
||||||
|
@name = name.gsub(/'|"/, "") #'
|
||||||
|
self.comment = comment
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# an included module
|
||||||
|
class Include < CodeObject
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
def initialize(name, comment)
|
||||||
|
super()
|
||||||
|
@name = name
|
||||||
|
self.comment = comment
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
333
lib/rdoc/diagram.rb
Normal file
333
lib/rdoc/diagram.rb
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
# 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 "dot/dot"
|
||||||
|
require 'rdoc/options'
|
||||||
|
|
||||||
|
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
|
||||||
|
File.makedirs(DOT_PATH)
|
||||||
|
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::DOTDigraph.new('name' => 'TopLevel',
|
||||||
|
'label' => i.file_absolute_name,
|
||||||
|
'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::DOTNode.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, i.name)
|
||||||
|
|
||||||
|
# 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::DOTDigraph.new('name' => 'TopLevel',
|
||||||
|
'label' => i.full_name,
|
||||||
|
'fontname' => FONT,
|
||||||
|
'fontsize' => '8',
|
||||||
|
'bgcolor' => 'lightcyan1',
|
||||||
|
'compound' => 'true')
|
||||||
|
|
||||||
|
graph << DOT::DOTNode.new('name' => 'node',
|
||||||
|
'fontname' => FONT,
|
||||||
|
'color' => 'black',
|
||||||
|
'fontsize' => 8)
|
||||||
|
draw_module(mod, graph, true)
|
||||||
|
mod.diagram = convert_to_png("m_#{file_count}_#{count}",
|
||||||
|
graph,
|
||||||
|
"Module: #{mod.name}")
|
||||||
|
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::DOTSubgraph.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 |m|
|
||||||
|
m_full_name = find_full_name(m.name, mod)
|
||||||
|
if @local_names.include?(m_full_name)
|
||||||
|
@global_graph << DOT::DOTEdge.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::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
|
||||||
|
'shape' => 'box',
|
||||||
|
'label' => "#{m_full_name}",
|
||||||
|
'URL' => %{"#{url}"})
|
||||||
|
@global_names << m_full_name
|
||||||
|
end
|
||||||
|
@global_graph << DOT::DOTEdge.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.instance.fileboxes
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
|
||||||
|
# create dummy node (needed if empty and for module includes)
|
||||||
|
if container.full_name
|
||||||
|
graph << DOT::DOTNode.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::DOTSubgraph.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::DOTNode.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::DOTEdge.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::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
|
||||||
|
'shape' => 'box',
|
||||||
|
'label' => "#{m_full_name}",
|
||||||
|
'URL' => %{"#{url}"})
|
||||||
|
@global_names << m_full_name
|
||||||
|
end
|
||||||
|
@global_graph << DOT::DOTEdge.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::DOTNode.new(
|
||||||
|
'name' => "#{sclass_full_name.gsub( /:/, '_' )}",
|
||||||
|
'label' => sclass_full_name,
|
||||||
|
'URL' => %{"#{url}"})
|
||||||
|
@global_names << sclass_full_name
|
||||||
|
end
|
||||||
|
@global_graph << DOT::DOTEdge.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, name)
|
||||||
|
op_type = Options.instance.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 << graph.to_s << "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
system "dot -T#{op_type} #{src} -o #{dot}"
|
||||||
|
|
||||||
|
# Now construct the imagemap wrapper around
|
||||||
|
# that png
|
||||||
|
|
||||||
|
return wrap_in_image_map(src, dot, name)
|
||||||
|
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, name)
|
||||||
|
res = %{<map name="map">\n}
|
||||||
|
dot_map = `dot -Tismap #{src}`
|
||||||
|
dot_map.each do |area|
|
||||||
|
unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
|
||||||
|
$stderr.puts "Unexpected output from dot:\n#{area}"
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
blx = $1; bly = $2
|
||||||
|
trx = $3; try = $4
|
||||||
|
url = $5; area_name = $6
|
||||||
|
res << %{ <area shape="RECT" coords="#{blx},#{try},#{trx},#{bly}" }
|
||||||
|
res << %{ href="#{url}" alt="#{area_name}">\n}
|
||||||
|
end
|
||||||
|
res << "</map>\n"
|
||||||
|
# map_file = src.sub(/.dot/, '.map')
|
||||||
|
# system("dot -Timap #{src} -o #{map_file}")
|
||||||
|
res << %{<img src="#{dot}" usemap="#map" border=0 alt="#{name}">}
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
112
lib/rdoc/generators/chm_generator.rb
Normal file
112
lib/rdoc/generators/chm_generator.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
require 'rdoc/generators/html_generator'
|
||||||
|
|
||||||
|
module Generators
|
||||||
|
|
||||||
|
class CHMGenerator < HTMLGenerator
|
||||||
|
|
||||||
|
HHC_PATH = "c:\\Program Files\\HTML Help Workshop\\hhc.exe"
|
||||||
|
|
||||||
|
# Standard generator factory
|
||||||
|
def CHMGenerator.for(options)
|
||||||
|
CHMGenerator.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"
|
||||||
|
|
||||||
|
exit 99
|
||||||
|
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 = TemplatePage.new(RDoc::Page::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 = TemplatePage.new(RDoc::Page::CONTENTS)
|
||||||
|
File.open("contents.hhc", "w") do |f|
|
||||||
|
template.write_html_on(f, values)
|
||||||
|
end
|
||||||
|
|
||||||
|
values = { "index" => index }
|
||||||
|
template = TemplatePage.new(RDoc::Page::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
|
||||||
|
|
||||||
|
|
||||||
|
end
|
1370
lib/rdoc/generators/html_generator.rb
Normal file
1370
lib/rdoc/generators/html_generator.rb
Normal file
File diff suppressed because it is too large
Load diff
86
lib/rdoc/generators/template/chm/chm.rb
Normal file
86
lib/rdoc/generators/template/chm/chm.rb
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
module RDoc
|
||||||
|
module Page
|
||||||
|
|
||||||
|
require "rdoc/generators/template/html/html"
|
||||||
|
|
||||||
|
# This is a nasty little hack, but hhc doesn't support the <?xml
|
||||||
|
# tag, so...
|
||||||
|
|
||||||
|
BODY.sub!(/<\?xml.*\?>/, '')
|
||||||
|
|
||||||
|
HPP_FILE = %{
|
||||||
|
[OPTIONS]
|
||||||
|
Auto Index = Yes
|
||||||
|
Compatibility=1.1 or later
|
||||||
|
Compiled file=%opname%.chm
|
||||||
|
Contents file=contents.hhc
|
||||||
|
Full-text search=Yes
|
||||||
|
Index file=index.hhk
|
||||||
|
Language=0x409 English(United States)
|
||||||
|
Title=%title%
|
||||||
|
|
||||||
|
[FILES]
|
||||||
|
START:all_html_files
|
||||||
|
%html_file_name%
|
||||||
|
END:all_html_files
|
||||||
|
}
|
||||||
|
|
||||||
|
CONTENTS = %{
|
||||||
|
<!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>
|
||||||
|
START:contents
|
||||||
|
<LI> <OBJECT type="text/sitemap">
|
||||||
|
<param name="Name" value="%c_name%">
|
||||||
|
<param name="Local" value="%ref%">
|
||||||
|
</OBJECT>
|
||||||
|
IF:methods
|
||||||
|
<ul>
|
||||||
|
START:methods
|
||||||
|
<LI> <OBJECT type="text/sitemap">
|
||||||
|
<param name="Name" value="%name%">
|
||||||
|
<param name="Local" value="%aref%">
|
||||||
|
</OBJECT>
|
||||||
|
END:methods
|
||||||
|
</ul>
|
||||||
|
ENDIF:methods
|
||||||
|
</LI>
|
||||||
|
END:contents
|
||||||
|
</UL>
|
||||||
|
</BODY></HTML>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CHM_INDEX = %{
|
||||||
|
<!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>
|
||||||
|
START:index
|
||||||
|
<LI> <OBJECT type="text/sitemap">
|
||||||
|
<param name="Name" value="%name%">
|
||||||
|
<param name="Local" value="%aref%">
|
||||||
|
</OBJECT>
|
||||||
|
END:index
|
||||||
|
</UL>
|
||||||
|
</BODY></HTML>
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
631
lib/rdoc/generators/template/html/css2.rb
Normal file
631
lib/rdoc/generators/template/html/css2.rb
Normal file
|
@ -0,0 +1,631 @@
|
||||||
|
#
|
||||||
|
# = CSS2 RDoc HTML template
|
||||||
|
#
|
||||||
|
# This is a template for RDoc that uses XHTML 1.0 Transitional 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.
|
||||||
|
#
|
||||||
|
# == 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
|
||||||
|
module Page
|
||||||
|
|
||||||
|
FONTS = "Verdana,Arial,Helvetica,sans-serif"
|
||||||
|
|
||||||
|
STYLE = %{
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
||||||
|
h1 { font-size: 120%; }
|
||||||
|
h2,h3,h4 { margin-top: 1em; }
|
||||||
|
|
||||||
|
a { background: #eef; color: #039; text-decoration: none; }
|
||||||
|
a:hover { background: #039; color: #eef; }
|
||||||
|
|
||||||
|
/* Override the base stylesheet's Anchor inside a table cell */
|
||||||
|
td > a {
|
||||||
|
background: transparent;
|
||||||
|
color: #039;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Structural elements =================================== */
|
||||||
|
|
||||||
|
div#index {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#index a {
|
||||||
|
margin-left: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#classHeader {
|
||||||
|
width: auto;
|
||||||
|
background: #039;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5em 1.5em 0.5em 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: 3px solid #006;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#classHeader a {
|
||||||
|
background: inherit;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#classHeader td {
|
||||||
|
background: inherit;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#fileHeader {
|
||||||
|
width: auto;
|
||||||
|
background: #039;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5em 1.5em 0.5em 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: 3px solid #006;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#fileHeader a {
|
||||||
|
background: inherit;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#fileHeader td {
|
||||||
|
background: inherit;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#bodyContent {
|
||||||
|
padding: 0 1.5em 0 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#description {
|
||||||
|
padding: 0.5em 1.5em;
|
||||||
|
background: #efefef;
|
||||||
|
border: 1px dotted #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#description h1,h2,h3,h4,h5,h6 {
|
||||||
|
color: black;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#validator-badges {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div#validator-badges img { border: 0; }
|
||||||
|
|
||||||
|
div#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 {
|
||||||
|
background: #eee;
|
||||||
|
color: #333;
|
||||||
|
padding: 3px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-aligned-row { vertical-align: vertical-align: top }
|
||||||
|
|
||||||
|
/* --- Context section classes ----------------------- */
|
||||||
|
|
||||||
|
.context-row { }
|
||||||
|
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
||||||
|
.context-item-value { font-size: x-small; color: #448; }
|
||||||
|
.context-item-desc { background: #efefef; }
|
||||||
|
|
||||||
|
/* --- Method classes -------------------------- */
|
||||||
|
.method-detail {
|
||||||
|
background: #EFEFEF;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
border: 1px dotted #DDD;
|
||||||
|
}
|
||||||
|
.method-heading {
|
||||||
|
color: black;
|
||||||
|
background: #AAA;
|
||||||
|
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 -------------------- */
|
||||||
|
|
||||||
|
a.source-toggle { font-size: 90%; }
|
||||||
|
div.method-source-code {
|
||||||
|
background: #262626;
|
||||||
|
color: #ffdead;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
border: 1px dashed #999;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
||||||
|
|
||||||
|
/* --- Ruby keyword styles --------------------- */
|
||||||
|
/* (requires a hacked html_generator.rb to add more class-types) */
|
||||||
|
.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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### H E A D E R T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
|
||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
"DTD/xhtml1-transitional.dtd">
|
||||||
|
}
|
||||||
|
|
||||||
|
HEADER = XHTML_PREAMBLE + %{
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
||||||
|
<meta http-equiv="Content-Script-Type" content="text/javascript" />
|
||||||
|
<link rel="stylesheet" href="%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>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### C O N T E X T C O N T E N T T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
CONTEXT_CONTENT = %{
|
||||||
|
<div id="contextContent">
|
||||||
|
IF:diagram
|
||||||
|
<div id="diagram">
|
||||||
|
%diagram%
|
||||||
|
</div>
|
||||||
|
ENDIF:diagram
|
||||||
|
|
||||||
|
IF:description
|
||||||
|
<div id="description">
|
||||||
|
%description%
|
||||||
|
</div>
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
<div id="requires-list">
|
||||||
|
<h2 class="section-bar">Required files</h2>
|
||||||
|
|
||||||
|
<div class="name-list">
|
||||||
|
START:requires
|
||||||
|
HREF:aref:name:
|
||||||
|
END:requires
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:requires
|
||||||
|
|
||||||
|
IF:methods
|
||||||
|
<div id="method-list">
|
||||||
|
<h2 class="section-bar">Methods</h2>
|
||||||
|
|
||||||
|
<div class="name-list">
|
||||||
|
START:methods
|
||||||
|
HREF:aref:name:
|
||||||
|
END:methods
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:methods
|
||||||
|
|
||||||
|
IF:constants
|
||||||
|
<div id="constants-list">
|
||||||
|
<h2 class="section-bar">Constants</h2>
|
||||||
|
|
||||||
|
<div class="name-list">
|
||||||
|
<table summary="Constants">
|
||||||
|
START:constants
|
||||||
|
<tr class="top-aligned-row context-row">
|
||||||
|
<td class="context-item-name">%name%</td>
|
||||||
|
<td>=</td>
|
||||||
|
<td class="context-item-value">%value%</td>
|
||||||
|
</tr>
|
||||||
|
IF:desc
|
||||||
|
<tr class="top-aligned-row context-row">
|
||||||
|
<td> </td>
|
||||||
|
<td colspan="2" class="context-item-desc">%desc%</td>
|
||||||
|
</tr>
|
||||||
|
ENDIF:desc
|
||||||
|
END:constants
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:constants
|
||||||
|
|
||||||
|
IF:aliases
|
||||||
|
<div id="aliases-list">
|
||||||
|
<h2 class="section-bar">External Aliases</h2>
|
||||||
|
|
||||||
|
<div class="name-list">
|
||||||
|
START:aliases
|
||||||
|
%old_name% -> %new_name% <br />
|
||||||
|
END:aliases
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:aliases
|
||||||
|
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
<div id="attribute-list">
|
||||||
|
<h2 class="section-bar">Attributes</h2>
|
||||||
|
|
||||||
|
<div class="name-list">
|
||||||
|
<table>
|
||||||
|
START:attributes
|
||||||
|
<tr class="top-aligned-row context-row">
|
||||||
|
<td class="context-item-name">%name%</td>
|
||||||
|
<td class="context-item-value"> [%rw%] </td>
|
||||||
|
<td class="context-item-desc">%a_desc%</td>
|
||||||
|
</tr>
|
||||||
|
END:attributes
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:classlist
|
||||||
|
<div id="class-list">
|
||||||
|
<h2 class="section-bar">Classes and Modules</h2>
|
||||||
|
|
||||||
|
%classlist%
|
||||||
|
</div>
|
||||||
|
ENDIF:classlist
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### F O O T E R T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
FOOTER = %{
|
||||||
|
<div id="validator-badges">
|
||||||
|
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### F I L E P A G E H E A D E R T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
FILE_PAGE = %{
|
||||||
|
<div id="fileHeader">
|
||||||
|
<h1>%short_name%</h1>
|
||||||
|
<table class="header-table">
|
||||||
|
<tr class="top-aligned-row">
|
||||||
|
<td><strong>Path:</strong></td>
|
||||||
|
<td>%full_path%
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="top-aligned-row">
|
||||||
|
<td><strong>Last Update:</strong></td>
|
||||||
|
<td>%dtm_modified%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### 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 = %{
|
||||||
|
<div id="classHeader">
|
||||||
|
<h1>%full_name% <sup class="type-note">(%classmod%)</sup></h1>
|
||||||
|
<table class="header-table">
|
||||||
|
<tr class="top-aligned-row">
|
||||||
|
<td><strong>In:</strong></td>
|
||||||
|
<td>
|
||||||
|
START:infiles
|
||||||
|
IF:full_path_url
|
||||||
|
<a href="%full_path_url%">
|
||||||
|
ENDIF:full_path_url
|
||||||
|
%full_path%
|
||||||
|
IF:full_path_url
|
||||||
|
</a>
|
||||||
|
ENDIF:full_path_url
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
<br />
|
||||||
|
END:infiles
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
IF:parent
|
||||||
|
<tr class="top-aligned-row">
|
||||||
|
<td><strong>Parent:</strong></td>
|
||||||
|
<td>
|
||||||
|
IF:par_url
|
||||||
|
<a href="%par_url%">
|
||||||
|
ENDIF:par_url
|
||||||
|
%parent%
|
||||||
|
IF:par_url
|
||||||
|
</a>
|
||||||
|
ENDIF:par_url
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
ENDIF:parent
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### M E T H O D L I S T T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
METHOD_LIST = %{
|
||||||
|
|
||||||
|
<!-- if includes -->
|
||||||
|
IF:includes
|
||||||
|
<div id="includes">
|
||||||
|
<h2 class="section-bar">Included Modules</h2>
|
||||||
|
|
||||||
|
<div id="includes-list">
|
||||||
|
START:includes
|
||||||
|
<span class="include-name">HREF:aref:name:</span>
|
||||||
|
END:includes
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
|
||||||
|
<!-- if method_list -->
|
||||||
|
IF:method_list
|
||||||
|
<div id="methods">
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
<h2 class="section-bar">%type% %category% methods</h2>
|
||||||
|
|
||||||
|
START:methods
|
||||||
|
<!-- %name%%params% -->
|
||||||
|
<div id="method-%aref%" class="method-detail">
|
||||||
|
<a name="%aref%"></a>
|
||||||
|
|
||||||
|
<div class="method-heading">
|
||||||
|
IF:codeurl
|
||||||
|
<a href="%codeurl%" target="Code" class="method-signature"
|
||||||
|
onclick="popupCode('%codeurl%');return false;">
|
||||||
|
ENDIF:codeurl
|
||||||
|
IF:sourcecode
|
||||||
|
<a href="#%aref%" class="method-signature">
|
||||||
|
ENDIF:sourcecode
|
||||||
|
<span class="method-name">%name%</span><span class="method-args">%params%</span>
|
||||||
|
IF:codeurl
|
||||||
|
</a>
|
||||||
|
ENDIF:codeurl
|
||||||
|
IF:sourcecode
|
||||||
|
</a>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="method-description">
|
||||||
|
IF:m_desc
|
||||||
|
%m_desc%
|
||||||
|
ENDIF:m_desc
|
||||||
|
IF:sourcecode
|
||||||
|
<p><a class="source-toggle" href="#"
|
||||||
|
onclick="toggleCode('%aref%-source');return false;">[Source]</a></p>
|
||||||
|
<div class="method-source-code" id="%aref%-source">
|
||||||
|
<pre>
|
||||||
|
%sourcecode%
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
|
||||||
|
</div>
|
||||||
|
ENDIF:method_list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### B O D Y T E M P L A T E
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
BODY = HEADER + %{
|
||||||
|
|
||||||
|
!INCLUDE! <!-- banner header -->
|
||||||
|
|
||||||
|
<div id="bodyContent">
|
||||||
|
|
||||||
|
} + CONTEXT_CONTENT + 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_PREAMBLE + %{
|
||||||
|
<!--
|
||||||
|
|
||||||
|
%title%
|
||||||
|
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
||||||
|
<link rel="stylesheet" href="http://www.FaerieMUD.org/stylesheets/rdoc.css" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>%code%</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### I N D E X F I L E T E M P L A T E S
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
FR_INDEX_BODY = %{
|
||||||
|
!INCLUDE!
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_INDEX = XHTML_PREAMBLE + %{
|
||||||
|
<!--
|
||||||
|
|
||||||
|
%list_title%
|
||||||
|
|
||||||
|
-->
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>%list_title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
||||||
|
<link rel="stylesheet" href="%style_url%" type="text/css" />
|
||||||
|
<base target="docwin" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="index">
|
||||||
|
<h1 class="section-bar">%list_title%</h1>
|
||||||
|
<div id="index-entries">
|
||||||
|
START:entries
|
||||||
|
<a href="%href%">%name%</a><br />
|
||||||
|
END:entries
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
CLASS_INDEX = FILE_INDEX
|
||||||
|
METHOD_INDEX = FILE_INDEX
|
||||||
|
|
||||||
|
INDEX = %{<?xml version="1.0" encoding="%charset%"?>
|
||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
||||||
|
"DTD/xhtml1-frameset.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
%title%
|
||||||
|
|
||||||
|
-->
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%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="%initial_page%" name="docwin" />
|
||||||
|
</frameset>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
end # module Page
|
||||||
|
end # class RDoc
|
||||||
|
|
418
lib/rdoc/generators/template/html/hefss.rb
Normal file
418
lib/rdoc/generators/template/html/hefss.rb
Normal file
|
@ -0,0 +1,418 @@
|
||||||
|
module RDoc
|
||||||
|
module Page
|
||||||
|
|
||||||
|
|
||||||
|
FONTS = "Verdana, Arial, Helvetica, sans-serif"
|
||||||
|
|
||||||
|
STYLE = %{
|
||||||
|
body,p { font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
color: #000040; background: #BBBBBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
td { font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
color: #000040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attr-rw { font-size: small; color: #444488 }
|
||||||
|
|
||||||
|
.title-row {color: #eeeeff;
|
||||||
|
background: #BBBBDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-title-font { color: white;
|
||||||
|
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
font-size: large;
|
||||||
|
height: 50px}
|
||||||
|
|
||||||
|
.small-title-font { color: purple;
|
||||||
|
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
font-size: small; }
|
||||||
|
|
||||||
|
.aqua { color: purple }
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-title {
|
||||||
|
font-size: medium;
|
||||||
|
font-weight: bold;
|
||||||
|
text_decoration: none;
|
||||||
|
padding: 3px 3px 3px 20px;
|
||||||
|
color: #3333CC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-name {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: medium;
|
||||||
|
text_decoration: none;
|
||||||
|
padding: 3px 3px 3px 20px;
|
||||||
|
color: #0000AA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-name {
|
||||||
|
font-size: medium;
|
||||||
|
font-weight: medium;
|
||||||
|
font-family: monospace;
|
||||||
|
text_decoration: none;
|
||||||
|
padding: 3px 3px 3px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paramsig {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.srcbut { float: right }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
BODY = %{
|
||||||
|
<html><head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<link rel=StyleSheet href="%style_url%" type="text/css" media=screen>
|
||||||
|
<script type="text/javascript" language="JavaScript">
|
||||||
|
<!--
|
||||||
|
function popCode(url) {
|
||||||
|
parent.frames.source.location = url
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body bgcolor="#BBBBBB">
|
||||||
|
|
||||||
|
!INCLUDE! <!-- banner header -->
|
||||||
|
|
||||||
|
IF:diagram
|
||||||
|
<table width="100%"><tr><td align="center">
|
||||||
|
%diagram%
|
||||||
|
</td></tr></table>
|
||||||
|
ENDIF:diagram
|
||||||
|
|
||||||
|
IF:description
|
||||||
|
<div class="description">%description%</div>
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Required files</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:requires
|
||||||
|
HREF:aref:name:
|
||||||
|
END:requires
|
||||||
|
ENDIF:requires
|
||||||
|
</div>
|
||||||
|
|
||||||
|
IF:methods
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Subroutines and Functions</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:methods
|
||||||
|
HREF:aref:name:,
|
||||||
|
END:methods
|
||||||
|
</div>
|
||||||
|
ENDIF:methods
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Arguments</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<table cellspacing=5>
|
||||||
|
START:attributes
|
||||||
|
<tr valign="top">
|
||||||
|
IF:rw
|
||||||
|
<td align="center" class="attr-rw"> [%rw%] </td>
|
||||||
|
ENDIF:rw
|
||||||
|
IFNOT:rw
|
||||||
|
<td></td>
|
||||||
|
ENDIF:rw
|
||||||
|
<td class="attr-name">%name%</td>
|
||||||
|
<td>%a_desc%</td>
|
||||||
|
</tr>
|
||||||
|
END:attributes
|
||||||
|
</table>
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:classlist
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Modules</td></tr>
|
||||||
|
</table><br>
|
||||||
|
%classlist%<br>
|
||||||
|
ENDIF:classlist
|
||||||
|
|
||||||
|
!INCLUDE! <!-- method descriptions -->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
FILE_PAGE = <<_FILE_PAGE_
|
||||||
|
<table width="100%">
|
||||||
|
<tr class="title-row">
|
||||||
|
<td><table width="100%"><tr>
|
||||||
|
<td class="big-title-font" colspan=2><font size=-3><B>File</B><BR></font>%short_name%</td>
|
||||||
|
<td align="right"><table cellspacing=0 cellpadding=2>
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Path:</td>
|
||||||
|
<td class="small-title-font">%full_path%
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Modified:</td>
|
||||||
|
<td class="small-title-font">%dtm_modified%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td></tr></table></td>
|
||||||
|
</tr>
|
||||||
|
</table><br>
|
||||||
|
_FILE_PAGE_
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
CLASS_PAGE = %{
|
||||||
|
<table width="100%" border=0 cellspacing=0>
|
||||||
|
<tr class="title-row">
|
||||||
|
<td class="big-title-font">
|
||||||
|
<font size=-3><B>%classmod%</B><BR></font>%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">
|
||||||
|
START:infiles
|
||||||
|
HREF:full_path_url:full_path:
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
END:infiles
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
IF:parent
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Parent:</td>
|
||||||
|
<td class="small-title-font">
|
||||||
|
IF:par_url
|
||||||
|
<a href="%par_url%" class="cyan">
|
||||||
|
ENDIF:par_url
|
||||||
|
%parent%
|
||||||
|
IF:par_url
|
||||||
|
</a>
|
||||||
|
ENDIF:par_url
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
ENDIF:parent
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table><br>
|
||||||
|
}
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
METHOD_LIST = %{
|
||||||
|
IF:includes
|
||||||
|
<div class="tablesubsubtitle">Uses</div><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:includes
|
||||||
|
<span class="method-name">HREF:aref:name:</span>
|
||||||
|
END:includes
|
||||||
|
</div>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
IF:method_list
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
|
||||||
|
</table>
|
||||||
|
START:methods
|
||||||
|
<table width="100%" cellspacing = 0 cellpadding=5 border=0>
|
||||||
|
<tr><td class="methodtitle">
|
||||||
|
<a name="%aref%">
|
||||||
|
<b>%name%</b>%params%
|
||||||
|
IF:codeurl
|
||||||
|
<a href="%codeurl%" target="source" class="srclink">src</a>
|
||||||
|
ENDIF:codeurl
|
||||||
|
</a></td></tr>
|
||||||
|
</table>
|
||||||
|
IF:m_desc
|
||||||
|
<div class="description">
|
||||||
|
%m_desc%
|
||||||
|
</div>
|
||||||
|
ENDIF:m_desc
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
ENDIF:method_list
|
||||||
|
}
|
||||||
|
|
||||||
|
=begin
|
||||||
|
=end
|
||||||
|
|
||||||
|
########################## Source code ##########################
|
||||||
|
|
||||||
|
SRC_PAGE = %{
|
||||||
|
<html>
|
||||||
|
<head><title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<style>
|
||||||
|
.kw { color: #3333FF; font-weight: bold }
|
||||||
|
.cmt { color: green; font-style: italic }
|
||||||
|
.str { color: #662222; font-style: italic }
|
||||||
|
.re { color: #662222; }
|
||||||
|
.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 }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body bgcolor="#BBBBBB">
|
||||||
|
<pre>%code%</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
########################## Index ################################
|
||||||
|
|
||||||
|
FR_INDEX_BODY = %{
|
||||||
|
!INCLUDE!
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_INDEX = %{
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<style>
|
||||||
|
<!--
|
||||||
|
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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
<base target="docwin">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="banner">%list_title%</div>
|
||||||
|
START:entries
|
||||||
|
<a href="%href%">%name%</a><br>
|
||||||
|
END:entries
|
||||||
|
</body></html>
|
||||||
|
}
|
||||||
|
|
||||||
|
CLASS_INDEX = FILE_INDEX
|
||||||
|
METHOD_INDEX = FILE_INDEX
|
||||||
|
|
||||||
|
INDEX = %{
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%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="Modules">
|
||||||
|
<frame src="fr_method_index.html" name="Subroutines and Functions">
|
||||||
|
</frameset>
|
||||||
|
<frameset rows="80%,20%">
|
||||||
|
<frame src="%initial_page%" name="docwin">
|
||||||
|
<frame src="blank.html" name="source">
|
||||||
|
</frameset>
|
||||||
|
<noframes>
|
||||||
|
<body bgcolor="#BBBBBB">
|
||||||
|
Click <a href="html/index.html">here</a> for a non-frames
|
||||||
|
version of this page.
|
||||||
|
</body>
|
||||||
|
</noframes>
|
||||||
|
</frameset>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
# and a blank page to use as a target
|
||||||
|
BLANK = %{
|
||||||
|
<html><body bgcolor="#BBBBBB"></body></html>
|
||||||
|
}
|
||||||
|
|
||||||
|
def write_extra_pages
|
||||||
|
template = TemplatePage.new(BLANK)
|
||||||
|
File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
762
lib/rdoc/generators/template/html/html.rb
Normal file
762
lib/rdoc/generators/template/html/html.rb
Normal file
|
@ -0,0 +1,762 @@
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
# This is how you define the HTML that RDoc generates. Simply create
|
||||||
|
# a file in rdoc/generators/html_templates that creates the
|
||||||
|
# module RDoc::Page and populate it as described below. Then invoke
|
||||||
|
# rdoc using the --template <name of your file> option, and
|
||||||
|
# your template will be used.
|
||||||
|
#
|
||||||
|
# The constants defining pages use a simple templating system:
|
||||||
|
#
|
||||||
|
# * The templating system is passed a hash. Keys in the hash correspond
|
||||||
|
# to tags on this page. The tag %abc% is looked up in the hash,
|
||||||
|
# and is replaced by the corresponding hash value.
|
||||||
|
#
|
||||||
|
# * Some tags are optional. You can detect this using IF/ENDIF
|
||||||
|
#
|
||||||
|
# IF: title
|
||||||
|
# The value of title is %title%
|
||||||
|
# ENDIF: title
|
||||||
|
#
|
||||||
|
# * Some entries in the hash have values that are arrays, where each
|
||||||
|
# entry in the array is itself a hash. These are used to generate
|
||||||
|
# lists using the START: construct. For example, given a hash
|
||||||
|
# containing
|
||||||
|
#
|
||||||
|
# { 'people' => [ { 'name' => 'Fred', 'age' => '12' },
|
||||||
|
# { 'name' => 'Mary', 'age' => '21' } ]
|
||||||
|
#
|
||||||
|
# You could generate a simple table using
|
||||||
|
#
|
||||||
|
# <table>
|
||||||
|
# START:people
|
||||||
|
# <tr><td>%name%<td>%age%</tr>
|
||||||
|
# END:people
|
||||||
|
# </table>
|
||||||
|
#
|
||||||
|
# These lists can be nested to an arbitrary depth
|
||||||
|
#
|
||||||
|
# * the construct HREF:url:name: generates <a href="%url%">%name%</a>
|
||||||
|
# if +url+ is defined in the hash, or %name% otherwise.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Your file must contain the following constants
|
||||||
|
#
|
||||||
|
# [*FONTS*] a list of fonts to be used
|
||||||
|
# [*STYLE*] a CSS section (without the <style> or comments). This is
|
||||||
|
# used to generate a style.css file
|
||||||
|
#
|
||||||
|
# [*BODY*]
|
||||||
|
# The main body of all non-index RDoc pages. BODY will contain
|
||||||
|
# two !INCLUDE!s. The first is used to include a document-type
|
||||||
|
# specific header (FILE_PAGE or CLASS_PAGE). The second include
|
||||||
|
# is for the method list (METHOD_LIST). THe body is passed:
|
||||||
|
#
|
||||||
|
# %title%::
|
||||||
|
# the page's title
|
||||||
|
#
|
||||||
|
# %style_url%::
|
||||||
|
# the url of a style sheet for this page
|
||||||
|
#
|
||||||
|
# %diagram%::
|
||||||
|
# the optional URL of a diagram for this page
|
||||||
|
#
|
||||||
|
# %description%::
|
||||||
|
# a (potentially multi-paragraph) string containing the
|
||||||
|
# description for th file/class/module.
|
||||||
|
#
|
||||||
|
# %requires%::
|
||||||
|
# an optional list of %aref%/%name% pairs, one for each module
|
||||||
|
# required by this file.
|
||||||
|
#
|
||||||
|
# %methods%::
|
||||||
|
# an optional list of %aref%/%name%, one for each method
|
||||||
|
# documented on this page. This is intended to be an index.
|
||||||
|
#
|
||||||
|
# %attributes%::
|
||||||
|
# An optional list. For each attribute it contains:
|
||||||
|
# %name%:: the attribute name
|
||||||
|
# %rw%:: r/o, w/o, or r/w
|
||||||
|
# %a_desc%:: description of the attribute
|
||||||
|
#
|
||||||
|
# %classlist%::
|
||||||
|
# An optional string containing an already-formatted list of
|
||||||
|
# classes and modules documented in this file
|
||||||
|
#
|
||||||
|
# For FILE_PAGE entries, the body will be passed
|
||||||
|
#
|
||||||
|
# %short_name%::
|
||||||
|
# The name of the file
|
||||||
|
#
|
||||||
|
# %full_path%::
|
||||||
|
# The full path to the file
|
||||||
|
#
|
||||||
|
# %dtm_modified%::
|
||||||
|
# The date/time the file was last changed
|
||||||
|
#
|
||||||
|
# For class and module pages, the body will be passed
|
||||||
|
#
|
||||||
|
# %classmod%::
|
||||||
|
# The name of the class or module
|
||||||
|
#
|
||||||
|
# %files%::
|
||||||
|
# A list. For each file this class is defined in, it contains:
|
||||||
|
# %full_path_url%:: an (optional) URL of the RDoc page
|
||||||
|
# for this file
|
||||||
|
# %full_path%:: the name of the file
|
||||||
|
#
|
||||||
|
# %par_url%::
|
||||||
|
# The (optional) URL of the RDoc page documenting this class's
|
||||||
|
# parent class
|
||||||
|
#
|
||||||
|
# %parent%::
|
||||||
|
# The name of this class's parent.
|
||||||
|
#
|
||||||
|
# For both files and classes, the body is passed the following information
|
||||||
|
# on includes and methods:
|
||||||
|
#
|
||||||
|
# %includes%::
|
||||||
|
# Optional list of included modules. For each, it receives
|
||||||
|
# %aref%:: optional URL to RDoc page for the module
|
||||||
|
# %name%:: the name of the module
|
||||||
|
#
|
||||||
|
# %method_list%::
|
||||||
|
# Optional list of methods of a particular class and category.
|
||||||
|
#
|
||||||
|
# Each method list entry contains:
|
||||||
|
#
|
||||||
|
# %type%:: public/private/protected
|
||||||
|
# %category%:: instance/class
|
||||||
|
# %methods%:: a list of method descriptions
|
||||||
|
#
|
||||||
|
# Each method description contains:
|
||||||
|
#
|
||||||
|
# %aref%:: a target aref, used when referencing this method
|
||||||
|
# description. You should code this as <a name="%aref%">
|
||||||
|
# %codeurl%:: the optional URL to the page containing this method's
|
||||||
|
# source code.
|
||||||
|
# %name%:: the method's name
|
||||||
|
# %params%:: the method's parameters
|
||||||
|
# %m_desc%:: the (potentially multi-paragraph) description of
|
||||||
|
# this method.
|
||||||
|
#
|
||||||
|
# [*CLASS_PAGE*]
|
||||||
|
# Header for pages documenting classes and modules. See
|
||||||
|
# BODY above for the available parameters.
|
||||||
|
#
|
||||||
|
# [*FILE_PAGE*]
|
||||||
|
# Header for pages documenting files. See
|
||||||
|
# BODY above for the available parameters.
|
||||||
|
#
|
||||||
|
# [*METHOD_LIST*]
|
||||||
|
# Controls the display of the listing of methods. See BODY for
|
||||||
|
# parameters.
|
||||||
|
#
|
||||||
|
# [*INDEX*]
|
||||||
|
# The top-level index page. For a browser-like environment
|
||||||
|
# define a frame set that includes the file, class, and
|
||||||
|
# method indices. Passed
|
||||||
|
# %title%:: title of page
|
||||||
|
# %initial_page% :: url of initial page to display
|
||||||
|
#
|
||||||
|
# [*CLASS_INDEX*]
|
||||||
|
# Individual files for the three indexes. Passed:
|
||||||
|
# %index_url%:: URL of main index page
|
||||||
|
# %entries%:: List of
|
||||||
|
# %name%:: name of an index entry
|
||||||
|
# %href%:: url of corresponding page
|
||||||
|
# [*METHOD_INDEX*]
|
||||||
|
# Same as CLASS_INDEX for methods
|
||||||
|
#
|
||||||
|
# [*FILE_INDEX*]
|
||||||
|
# Same as CLASS_INDEX for methods
|
||||||
|
#
|
||||||
|
# [*FR_INDEX_BODY*]
|
||||||
|
# A wrapper around CLASS_INDEX, METHOD_INDEX, and FILE_INDEX.
|
||||||
|
# If those index strings contain the complete HTML for the
|
||||||
|
# output, then FR_INDEX_BODY can simply be !INCLUDE!
|
||||||
|
#
|
||||||
|
# [*SRC_PAGE*]
|
||||||
|
# Page used to display source code. Passed %title% and %code%,
|
||||||
|
# the latter being a multi-line string of code.
|
||||||
|
|
||||||
|
module Page
|
||||||
|
|
||||||
|
FONTS = "Verdana, Arial, Helvetica, sans-serif"
|
||||||
|
|
||||||
|
STYLE = %{
|
||||||
|
body,td,p { font-family: %fonts%;
|
||||||
|
color: #000040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attr-rw { font-size: x-small; color: #444488 }
|
||||||
|
|
||||||
|
.title-row { background: #0000aa;
|
||||||
|
color: #eeeeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-title-font { color: white;
|
||||||
|
font-family: %fonts%;
|
||||||
|
font-size: large;
|
||||||
|
height: 50px}
|
||||||
|
|
||||||
|
.small-title-font { color: aqua;
|
||||||
|
font-family: %fonts%;
|
||||||
|
font-size: xx-small; }
|
||||||
|
|
||||||
|
.aqua { color: aqua }
|
||||||
|
|
||||||
|
.method-name, attr-name {
|
||||||
|
font-family: monospace; font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tablesubtitle, .tablesubsubtitle {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1ex;
|
||||||
|
margin-bottom: .5ex;
|
||||||
|
padding: 5px 0px 5px 20px;
|
||||||
|
font-size: large;
|
||||||
|
color: aqua;
|
||||||
|
background: #3333cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-list {
|
||||||
|
font-family: monospace;
|
||||||
|
margin-left: 40px;
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
line-height: 140%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-left: 40px;
|
||||||
|
margin-top: -2ex;
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description p {
|
||||||
|
line-height: 140%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aka {
|
||||||
|
margin-left: 40px;
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
line-height: 100%;
|
||||||
|
font-size: small;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.methodtitle {
|
||||||
|
font-size: medium;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0000AA;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paramsig {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.srcbut { float: right }
|
||||||
|
|
||||||
|
pre { font-size: 1.2em; }
|
||||||
|
tt { font-size: 1.2em; }
|
||||||
|
|
||||||
|
pre.source {
|
||||||
|
border-style: groove;
|
||||||
|
background-color: #ddddff;
|
||||||
|
margin-left: 40px;
|
||||||
|
padding: 1em 0em 1em 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.classlist {
|
||||||
|
margin-left: 40px;
|
||||||
|
margin-bottom: 2ex;
|
||||||
|
line-height: 140%;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: list-item;
|
||||||
|
margin-top: .6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
HEADER = %{
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
||||||
|
<link rel=StyleSheet href="%style_url%" type="text/css" media="screen" />
|
||||||
|
<script type="text/javascript" language="JavaScript">
|
||||||
|
<!--
|
||||||
|
function popCode(url) {
|
||||||
|
window.open(url, "Code",
|
||||||
|
"resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
METHOD_LIST = %{
|
||||||
|
IF:includes
|
||||||
|
<table summary="Included modules" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Included modules</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="name-list">
|
||||||
|
START:includes
|
||||||
|
<span class="method-name">HREF:aref:name:</span>
|
||||||
|
END:includes
|
||||||
|
</div>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
IF:method_list
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
<table summary="Method list" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
|
||||||
|
</table>
|
||||||
|
START:methods
|
||||||
|
<table summary="method" width="100%" cellspacing="0" cellpadding="5" border="0">
|
||||||
|
<tr><td class="methodtitle">
|
||||||
|
<a name="%aref%"></a>
|
||||||
|
IF:codeurl
|
||||||
|
<a href="%codeurl%" target="Code" class="methodtitle"
|
||||||
|
onClick="popCode('%codeurl%');return false;">
|
||||||
|
ENDIF:codeurl
|
||||||
|
<b>%name%</b>%params%
|
||||||
|
IF:codeurl
|
||||||
|
</a>
|
||||||
|
ENDIF:codeurl
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
IF:m_desc
|
||||||
|
<div class="description">
|
||||||
|
%m_desc%
|
||||||
|
</div>
|
||||||
|
ENDIF:m_desc
|
||||||
|
IF:aka
|
||||||
|
<div class="aka">
|
||||||
|
This method is also aliased as
|
||||||
|
START:aka
|
||||||
|
<a href="%aref%">%name%</a>
|
||||||
|
END:aka
|
||||||
|
</div>
|
||||||
|
ENDIF:aka
|
||||||
|
IF:sourcecode
|
||||||
|
<pre class="source">
|
||||||
|
%sourcecode%
|
||||||
|
</pre>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
ENDIF:method_list
|
||||||
|
}
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
CONTEXT_CONTENT = %{
|
||||||
|
IF:diagram
|
||||||
|
<table summary="Diagram of classes and modules" width="100%">
|
||||||
|
<tr><td align="center">
|
||||||
|
%diagram%
|
||||||
|
</td></tr></table>
|
||||||
|
ENDIF:diagram
|
||||||
|
|
||||||
|
|
||||||
|
IF:description
|
||||||
|
<div class="description">%description%</div>
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
<table summary="Requires" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Required files</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="name-list">
|
||||||
|
START:requires
|
||||||
|
HREF:aref:name:
|
||||||
|
END:requires
|
||||||
|
</div>
|
||||||
|
ENDIF:requires
|
||||||
|
|
||||||
|
IF:methods
|
||||||
|
<table summary="Methods" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Methods</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="name-list">
|
||||||
|
START:methods
|
||||||
|
HREF:aref:name:
|
||||||
|
END:methods
|
||||||
|
</div>
|
||||||
|
ENDIF:methods
|
||||||
|
|
||||||
|
IF:constants
|
||||||
|
<table summary="Constants" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Constants</td></tr>
|
||||||
|
</table>
|
||||||
|
<table cellpadding="5">
|
||||||
|
START:constants
|
||||||
|
<tr valign="top"><td>%name%</td><td>=</td><td>%value%</td></tr>
|
||||||
|
IF:desc
|
||||||
|
<tr><td></td><td></td><td>%desc%</td></tr>
|
||||||
|
ENDIF:desc
|
||||||
|
END:constants
|
||||||
|
</table>
|
||||||
|
ENDIF:constants
|
||||||
|
|
||||||
|
IF:aliases
|
||||||
|
<table summary="Aliases" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">External Aliases</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="name-list">
|
||||||
|
START:aliases
|
||||||
|
%old_name% -> %new_name%<br />
|
||||||
|
END:aliases
|
||||||
|
</div>
|
||||||
|
ENDIF:aliases
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
<table summary="Attributes" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Attributes</td></tr>
|
||||||
|
</table>
|
||||||
|
<table summary="Attribute details" cellspacing="5">
|
||||||
|
START:attributes
|
||||||
|
<tr valign="top">
|
||||||
|
<td class="attr-name">%name%</td>
|
||||||
|
IF:rw
|
||||||
|
<td align="center" class="attr-rw"> [%rw%] </td>
|
||||||
|
ENDIF:rw
|
||||||
|
IFNOT:rw
|
||||||
|
<td></td>
|
||||||
|
ENDIF:rw
|
||||||
|
<td>%a_desc%</td>
|
||||||
|
</tr>
|
||||||
|
END:attributes
|
||||||
|
</table>
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:classlist
|
||||||
|
<table summary="List of classes" cellpadding="5" width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Classes and Modules</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="classlist">
|
||||||
|
%classlist%
|
||||||
|
</div>
|
||||||
|
ENDIF:classlist
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
BODY = HEADER + %{
|
||||||
|
<body bgcolor="white">
|
||||||
|
!INCLUDE! <!-- banner header -->
|
||||||
|
} +
|
||||||
|
CONTEXT_CONTENT + METHOD_LIST +
|
||||||
|
%{
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
FILE_PAGE = <<_FILE_PAGE_
|
||||||
|
<table summary="Information on file" width="100%">
|
||||||
|
<tr class="title-row">
|
||||||
|
<td><table summary="layout" width="100%"><tr>
|
||||||
|
<td class="big-title-font" colspan="2">%short_name%</td>
|
||||||
|
<td align="right"><table summary="layout" cellspacing="0" cellpadding="2">
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Path:</td>
|
||||||
|
<td class="small-title-font">%full_path%
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Modified:</td>
|
||||||
|
<td class="small-title-font">%dtm_modified%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td></tr></table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
_FILE_PAGE_
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
CLASS_PAGE = %{
|
||||||
|
<table summary="Information on class" width="100%" border="0" cellspacing="0">
|
||||||
|
<tr class="title-row">
|
||||||
|
<td class="big-title-font">
|
||||||
|
<sup><font color="aqua">%classmod%</font></sup> %full_name%
|
||||||
|
</td>
|
||||||
|
<td align="right">
|
||||||
|
<table summary="layout" cellspacing="0" cellpadding="2">
|
||||||
|
<tr valign="top">
|
||||||
|
<td class="small-title-font">In:</td>
|
||||||
|
<td class="small-title-font">
|
||||||
|
START:infiles
|
||||||
|
IF:full_path_url
|
||||||
|
<a href="%full_path_url%" class="aqua">
|
||||||
|
ENDIF:full_path_url
|
||||||
|
%full_path%
|
||||||
|
IF:full_path_url
|
||||||
|
</a>
|
||||||
|
ENDIF:full_path_url
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
<br />
|
||||||
|
END:infiles
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
IF:parent
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Parent:</td>
|
||||||
|
<td class="small-title-font">
|
||||||
|
IF:par_url
|
||||||
|
<a href="%par_url%" class="aqua">
|
||||||
|
ENDIF:par_url
|
||||||
|
%parent%
|
||||||
|
IF:par_url
|
||||||
|
</a>
|
||||||
|
ENDIF:par_url
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
ENDIF:parent
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
=begin
|
||||||
|
=end
|
||||||
|
|
||||||
|
########################## Source code ##########################
|
||||||
|
|
||||||
|
SRC_PAGE = %{
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<title>%title%</title>
|
||||||
|
<link rel=StyleSheet href="%style_url%" type="text/css" media="screen" />
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
<pre>%code%</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
########################## Index ################################
|
||||||
|
|
||||||
|
FR_INDEX_BODY = %{
|
||||||
|
!INCLUDE!
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_INDEX = %{
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<title>%list_title%</title>
|
||||||
|
<style type="text/css">
|
||||||
|
<!--
|
||||||
|
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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
A.xx { color: white; font-weight: bold; }
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
<base target="docwin">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="banner"><a href="%index_url%" class="xx">%list_title%</a></div>
|
||||||
|
START:entries
|
||||||
|
<a href="%href%">%name%</a><br />
|
||||||
|
END:entries
|
||||||
|
</body></html>
|
||||||
|
}
|
||||||
|
|
||||||
|
CLASS_INDEX = FILE_INDEX
|
||||||
|
METHOD_INDEX = FILE_INDEX
|
||||||
|
|
||||||
|
INDEX = %{
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<title>%title%</title></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="%initial_page%" name="docwin">
|
||||||
|
<noframes>
|
||||||
|
<body bgcolor="white">
|
||||||
|
Sorry, RDoc currently only generates HTML using frames.
|
||||||
|
</body>
|
||||||
|
</noframes>
|
||||||
|
</frameset>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
#
|
||||||
|
# The following is used for the -1 option
|
||||||
|
#
|
||||||
|
|
||||||
|
CONTENTS_XML = %{
|
||||||
|
IF:description
|
||||||
|
%description%
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
<h4>Requires:</h4>
|
||||||
|
<ul>
|
||||||
|
START:requires
|
||||||
|
IF:aref
|
||||||
|
<li><a href="%aref%">%name%</a></li>
|
||||||
|
ENDIF:aref
|
||||||
|
IFNOT:aref
|
||||||
|
<li>%name%</li>
|
||||||
|
ENDIF:aref
|
||||||
|
END:requires
|
||||||
|
</ul>
|
||||||
|
ENDIF:requires
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
<h4>Attributes</h4>
|
||||||
|
<table>
|
||||||
|
START:attributes
|
||||||
|
<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
|
||||||
|
END:attributes
|
||||||
|
</table>
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:includes
|
||||||
|
<h4>Includes</h4>
|
||||||
|
<ul>
|
||||||
|
START:includes
|
||||||
|
IF:aref
|
||||||
|
<li><a href="%aref%">%name%</a></li>
|
||||||
|
ENDIF:aref
|
||||||
|
IFNOT:aref
|
||||||
|
<li>%name%</li>
|
||||||
|
ENDIF:aref
|
||||||
|
END:includes
|
||||||
|
</ul>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
IF:method_list
|
||||||
|
<h3>Methods</h3>
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
START:methods
|
||||||
|
<h4>%type% %category% method: <a name="%aref%">%name%%params%</a></h4>
|
||||||
|
|
||||||
|
IF:m_desc
|
||||||
|
%m_desc%
|
||||||
|
ENDIF:m_desc
|
||||||
|
|
||||||
|
IF:sourcecode
|
||||||
|
<blockquote><pre>
|
||||||
|
%sourcecode%
|
||||||
|
</pre></blockquote>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
ENDIF:method_list
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
ONE_PAGE = %{
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
START:files
|
||||||
|
<h2>File: %short_name%</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td>Path:</td><td>%full_path%</td></tr>
|
||||||
|
<tr><td>Modified:</td><td>%dtm_modified%</td></tr>
|
||||||
|
</table>
|
||||||
|
} + CONTENTS_XML + %{
|
||||||
|
END:files
|
||||||
|
|
||||||
|
IF:classes
|
||||||
|
<h2>Classes</h2>
|
||||||
|
START:classes
|
||||||
|
IF:parent
|
||||||
|
<h3>%classmod% %full_name% < HREF:par_url:parent:</h3>
|
||||||
|
ENDIF:parent
|
||||||
|
IFNOT:parent
|
||||||
|
<h3>%classmod% %full_name%</h3>
|
||||||
|
ENDIF:parent
|
||||||
|
|
||||||
|
IF:infiles
|
||||||
|
(in files
|
||||||
|
START:infiles
|
||||||
|
HREF:full_path_url:full_path:
|
||||||
|
END:infiles
|
||||||
|
)
|
||||||
|
ENDIF:infiles
|
||||||
|
} + CONTENTS_XML + %{
|
||||||
|
END:classes
|
||||||
|
ENDIF:classes
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
398
lib/rdoc/generators/template/html/kilmer.rb
Normal file
398
lib/rdoc/generators/template/html/kilmer.rb
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
module RDoc
|
||||||
|
module Page
|
||||||
|
|
||||||
|
|
||||||
|
FONTS = "Verdana, Arial, Helvetica, sans-serif"
|
||||||
|
|
||||||
|
STYLE = %{
|
||||||
|
body,td,p { font-family: %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: %fonts%;
|
||||||
|
font-size: large;
|
||||||
|
height: 60px;
|
||||||
|
padding: 10px 3px 10px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-title-font { color: black;
|
||||||
|
font-family: %fonts%;
|
||||||
|
font-size:10; }
|
||||||
|
|
||||||
|
.aqua { color: black }
|
||||||
|
|
||||||
|
.method-name, .attr-name {
|
||||||
|
font-family: font-family: %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-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.srclink {
|
||||||
|
font-size: small;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0000DD;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paramsig {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.srcbut { float: right }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
BODY = %{
|
||||||
|
<html><head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<link rel=StyleSheet href="%style_url%" type="text/css" media=screen>
|
||||||
|
<script type="text/javascript" language="JavaScript">
|
||||||
|
<!--
|
||||||
|
function popCode(url) {
|
||||||
|
parent.frames.source.location = url
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
|
||||||
|
!INCLUDE! <!-- banner header -->
|
||||||
|
|
||||||
|
IF:diagram
|
||||||
|
<table width="100%"><tr><td align="center">
|
||||||
|
%diagram%
|
||||||
|
</td></tr></table>
|
||||||
|
ENDIF:diagram
|
||||||
|
|
||||||
|
IF:description
|
||||||
|
<div class="description">%description%</div>
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Required files</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:requires
|
||||||
|
HREF:aref:name:
|
||||||
|
END:requires
|
||||||
|
ENDIF:requires
|
||||||
|
</div>
|
||||||
|
|
||||||
|
IF:methods
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Methods</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:methods
|
||||||
|
HREF:aref:name:,
|
||||||
|
END:methods
|
||||||
|
</div>
|
||||||
|
ENDIF:methods
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Attributes</td></tr>
|
||||||
|
</table><br>
|
||||||
|
<table cellspacing=5>
|
||||||
|
START:attributes
|
||||||
|
<tr valign="top">
|
||||||
|
IF:rw
|
||||||
|
<td align="center" class="attr-rw"> [%rw%] </td>
|
||||||
|
ENDIF:rw
|
||||||
|
IFNOT:rw
|
||||||
|
<td></td>
|
||||||
|
ENDIF:rw
|
||||||
|
<td class="attr-name">%name%</td>
|
||||||
|
<td>%a_desc%</td>
|
||||||
|
</tr>
|
||||||
|
END:attributes
|
||||||
|
</table>
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:classlist
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">Classes and Modules</td></tr>
|
||||||
|
</table><br>
|
||||||
|
%classlist%<br>
|
||||||
|
ENDIF:classlist
|
||||||
|
|
||||||
|
!INCLUDE! <!-- method descriptions -->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
FILE_PAGE = <<_FILE_PAGE_
|
||||||
|
<table width="100%">
|
||||||
|
<tr class="title-row">
|
||||||
|
<td><table width="100%"><tr>
|
||||||
|
<td class="big-title-font" colspan=2><font size=-3><B>File</B><BR></font>%short_name%</td>
|
||||||
|
<td align="right"><table cellspacing=0 cellpadding=2>
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Path:</td>
|
||||||
|
<td class="small-title-font">%full_path%
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Modified:</td>
|
||||||
|
<td class="small-title-font">%dtm_modified%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td></tr></table></td>
|
||||||
|
</tr>
|
||||||
|
</table><br>
|
||||||
|
_FILE_PAGE_
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
CLASS_PAGE = %{
|
||||||
|
<table width="100%" border=0 cellspacing=0>
|
||||||
|
<tr class="title-row">
|
||||||
|
<td class="big-title-font">
|
||||||
|
<font size=-3><B>%classmod%</B><BR></font>%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">
|
||||||
|
START:infiles
|
||||||
|
HREF:full_path_url:full_path:
|
||||||
|
IF:cvsurl
|
||||||
|
(<a href="%cvsurl%">CVS</a>)
|
||||||
|
ENDIF:cvsurl
|
||||||
|
END:infiles
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
IF:parent
|
||||||
|
<tr>
|
||||||
|
<td class="small-title-font">Parent:</td>
|
||||||
|
<td class="small-title-font">
|
||||||
|
IF:par_url
|
||||||
|
<a href="%par_url%" class="cyan">
|
||||||
|
ENDIF:par_url
|
||||||
|
%parent%
|
||||||
|
IF:par_url
|
||||||
|
</a>
|
||||||
|
ENDIF:par_url
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
ENDIF:parent
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table><br>
|
||||||
|
}
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
METHOD_LIST = %{
|
||||||
|
IF:includes
|
||||||
|
<div class="tablesubsubtitle">Included modules</div><br>
|
||||||
|
<div class="name-list">
|
||||||
|
START:includes
|
||||||
|
<span class="method-name">HREF:aref:name:</span>
|
||||||
|
END:includes
|
||||||
|
</div>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
IF:method_list
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
<table cellpadding=5 width="100%">
|
||||||
|
<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
|
||||||
|
</table>
|
||||||
|
START:methods
|
||||||
|
<table width="100%" cellspacing = 0 cellpadding=5 border=0>
|
||||||
|
<tr><td class="methodtitle">
|
||||||
|
<a name="%aref%">
|
||||||
|
<b>%name%</b>%params%
|
||||||
|
IF:codeurl
|
||||||
|
<a href="%codeurl%" target="source" class="srclink">src</a>
|
||||||
|
ENDIF:codeurl
|
||||||
|
</a></td></tr>
|
||||||
|
</table>
|
||||||
|
IF:m_desc
|
||||||
|
<div class="description">
|
||||||
|
%m_desc%
|
||||||
|
</div>
|
||||||
|
ENDIF:m_desc
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
ENDIF:method_list
|
||||||
|
}
|
||||||
|
|
||||||
|
=begin
|
||||||
|
=end
|
||||||
|
|
||||||
|
########################## Source code ##########################
|
||||||
|
|
||||||
|
SRC_PAGE = %{
|
||||||
|
<html>
|
||||||
|
<head><title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<style>
|
||||||
|
.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 }
|
||||||
|
.kw { color: #3333FF; font-weight: bold }
|
||||||
|
.cmt { color: green; font-style: italic }
|
||||||
|
.str { color: #662222; font-style: italic }
|
||||||
|
.re { color: #662222; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
<pre>%code%</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
########################## Index ################################
|
||||||
|
|
||||||
|
FR_INDEX_BODY = %{
|
||||||
|
!INCLUDE!
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE_INDEX = %{
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
|
||||||
|
<style>
|
||||||
|
<!--
|
||||||
|
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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
-->
|
||||||
|
</style>
|
||||||
|
<base target="docwin">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="banner">%list_title%</div>
|
||||||
|
START:entries
|
||||||
|
<a href="%href%">%name%</a><br>
|
||||||
|
END:entries
|
||||||
|
</body></html>
|
||||||
|
}
|
||||||
|
|
||||||
|
CLASS_INDEX = FILE_INDEX
|
||||||
|
METHOD_INDEX = FILE_INDEX
|
||||||
|
|
||||||
|
INDEX = %{
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%title%</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=%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>
|
||||||
|
<frameset rows="80%,20%">
|
||||||
|
<frame src="%initial_page%" name="docwin">
|
||||||
|
<frame src="blank.html" name="source">
|
||||||
|
</frameset>
|
||||||
|
<noframes>
|
||||||
|
<body bgcolor="white">
|
||||||
|
Click <a href="html/index.html">here</a> for a non-frames
|
||||||
|
version of this page.
|
||||||
|
</body>
|
||||||
|
</noframes>
|
||||||
|
</frameset>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
# and a blank page to use as a target
|
||||||
|
BLANK = %{
|
||||||
|
<html><body bgcolor="white"></body></html>
|
||||||
|
}
|
||||||
|
|
||||||
|
def write_extra_pages
|
||||||
|
template = TemplatePage.new(BLANK)
|
||||||
|
File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
112
lib/rdoc/generators/template/xml/rdf.rb
Normal file
112
lib/rdoc/generators/template/xml/rdf.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
module RDoc
|
||||||
|
module Page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CONTENTS_RDF = %{
|
||||||
|
IF:description
|
||||||
|
<description rd:parseType="Literal">
|
||||||
|
%description%
|
||||||
|
</description>
|
||||||
|
ENDIF:description
|
||||||
|
|
||||||
|
IF:requires
|
||||||
|
START:requires
|
||||||
|
<rd:required-file rd:name="%name%" />
|
||||||
|
END:requires
|
||||||
|
ENDIF:requires
|
||||||
|
|
||||||
|
IF:attributes
|
||||||
|
START:attributes
|
||||||
|
<contents>
|
||||||
|
<Attribute rd:name="%name%">
|
||||||
|
IF:rw
|
||||||
|
<attribute-rw>%rw%</attribute-rw>
|
||||||
|
ENDIF:rw
|
||||||
|
<description rdf:parseType="Literal">%a_desc%</description>
|
||||||
|
</Attribute>
|
||||||
|
</contents>
|
||||||
|
END:attributes
|
||||||
|
ENDIF:attributes
|
||||||
|
|
||||||
|
IF:includes
|
||||||
|
<IncludedModuleList>
|
||||||
|
START:includes
|
||||||
|
<included-module rd:name="%name%" />
|
||||||
|
END:includes
|
||||||
|
</IncludedModuleList>
|
||||||
|
ENDIF:includes
|
||||||
|
|
||||||
|
IF:method_list
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
START:methods
|
||||||
|
<contents>
|
||||||
|
<Method rd:name="%name%" rd:visibility="%type%"
|
||||||
|
rd:category="%category%" rd:id="%aref%">
|
||||||
|
<parameters>%params%</parameters>
|
||||||
|
IF:m_desc
|
||||||
|
<description rdf:parseType="Literal">
|
||||||
|
%m_desc%
|
||||||
|
</description>
|
||||||
|
ENDIF:m_desc
|
||||||
|
IF:sourcecode
|
||||||
|
<source-code-listing rdf:parseType="Literal">
|
||||||
|
%sourcecode%
|
||||||
|
</source-code-listing>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
</Method>
|
||||||
|
</contents>
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
ENDIF:method_list
|
||||||
|
<!-- end method list -->
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
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 -->
|
||||||
|
START:files
|
||||||
|
<rd:File rd:name="%short_name%" rd:id="%href%">
|
||||||
|
<path>%full_path%</path>
|
||||||
|
<dtm-modified>%dtm_modified%</dtm-modified>
|
||||||
|
} + CONTENTS_RDF + %{
|
||||||
|
</rd:File>
|
||||||
|
END:files
|
||||||
|
START:classes
|
||||||
|
<%classmod% rd:name="%full_name%" rd:id="%full_name%">
|
||||||
|
<classmod-info>
|
||||||
|
IF:infiles
|
||||||
|
<InFiles>
|
||||||
|
START:infiles
|
||||||
|
<infile>
|
||||||
|
<File rd:name="%full_path%"
|
||||||
|
IF:full_path_url
|
||||||
|
rdf:about="%full_path_url%"
|
||||||
|
ENDIF:full_path_url
|
||||||
|
/>
|
||||||
|
</infile>
|
||||||
|
END:infiles
|
||||||
|
</InFiles>
|
||||||
|
ENDIF:infiles
|
||||||
|
IF:parent
|
||||||
|
<superclass>HREF:par_url:parent:</superclass>
|
||||||
|
ENDIF:parent
|
||||||
|
</classmod-info>
|
||||||
|
} + CONTENTS_RDF + %{
|
||||||
|
</%classmod%>
|
||||||
|
END:classes
|
||||||
|
<!-- /RDoc -->
|
||||||
|
</rdf:RDF>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
112
lib/rdoc/generators/template/xml/xml.rb
Normal file
112
lib/rdoc/generators/template/xml/xml.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
module RDoc
|
||||||
|
module Page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CONTENTS_XML = %{
|
||||||
|
IF:description
|
||||||
|
<description>
|
||||||
|
%description%
|
||||||
|
</description>
|
||||||
|
ENDIF:description
|
||||||
|
<contents>
|
||||||
|
IF:requires
|
||||||
|
<required-file-list>
|
||||||
|
START:requires
|
||||||
|
<required-file name="%name%"
|
||||||
|
IF:aref
|
||||||
|
href="%aref%"
|
||||||
|
ENDIF:aref
|
||||||
|
/>
|
||||||
|
END:requires
|
||||||
|
</required-file-list>
|
||||||
|
ENDIF:requires
|
||||||
|
IF:attributes
|
||||||
|
<attribute-list>
|
||||||
|
START:attributes
|
||||||
|
<attribute name="%name%">
|
||||||
|
IF:rw
|
||||||
|
<attribute-rw>%rw%</attribute-rw>
|
||||||
|
ENDIF:rw
|
||||||
|
<description>%a_desc%</description>
|
||||||
|
</attribute>
|
||||||
|
END:attributes
|
||||||
|
</attribute-list>
|
||||||
|
ENDIF:attributes
|
||||||
|
IF:includes
|
||||||
|
<included-module-list>
|
||||||
|
START:includes
|
||||||
|
<included-module name="%name%"
|
||||||
|
IF:aref
|
||||||
|
href="%aref%"
|
||||||
|
ENDIF:aref
|
||||||
|
/>
|
||||||
|
END:includes
|
||||||
|
</included-module-list>
|
||||||
|
ENDIF:includes
|
||||||
|
IF:method_list
|
||||||
|
<method-list>
|
||||||
|
START:method_list
|
||||||
|
IF:methods
|
||||||
|
START:methods
|
||||||
|
<method name="%name%" type="%type%" category="%category%" id="%aref%">
|
||||||
|
<parameters>%params%</parameters>
|
||||||
|
IF:m_desc
|
||||||
|
<description>
|
||||||
|
%m_desc%
|
||||||
|
</description>
|
||||||
|
ENDIF:m_desc
|
||||||
|
IF:sourcecode
|
||||||
|
<source-code-listing>
|
||||||
|
%sourcecode%
|
||||||
|
</source-code-listing>
|
||||||
|
ENDIF:sourcecode
|
||||||
|
</method>
|
||||||
|
END:methods
|
||||||
|
ENDIF:methods
|
||||||
|
END:method_list
|
||||||
|
</method-list>
|
||||||
|
ENDIF:method_list
|
||||||
|
</contents>
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rdoc>
|
||||||
|
<file-list>
|
||||||
|
START:files
|
||||||
|
<file name="%short_name%" id="%href%">
|
||||||
|
<file-info>
|
||||||
|
<path>%full_path%</path>
|
||||||
|
<dtm-modified>%dtm_modified%</dtm-modified>
|
||||||
|
</file-info>
|
||||||
|
} + CONTENTS_XML + %{
|
||||||
|
</file>
|
||||||
|
END:files
|
||||||
|
</file-list>
|
||||||
|
<class-module-list>
|
||||||
|
START:classes
|
||||||
|
<%classmod% name="%full_name%" id="%full_name%">
|
||||||
|
<classmod-info>
|
||||||
|
IF:infiles
|
||||||
|
<infiles>
|
||||||
|
START:infiles
|
||||||
|
<infile>HREF:full_path_url:full_path:</infile>
|
||||||
|
END:infiles
|
||||||
|
</infiles>
|
||||||
|
ENDIF:infiles
|
||||||
|
IF:parent
|
||||||
|
<superclass>HREF:par_url:parent:</superclass>
|
||||||
|
ENDIF:parent
|
||||||
|
</classmod-info>
|
||||||
|
} + CONTENTS_XML + %{
|
||||||
|
</%classmod%>
|
||||||
|
END:classes
|
||||||
|
</class-module-list>
|
||||||
|
</rdoc>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
130
lib/rdoc/generators/xml_generator.rb
Normal file
130
lib/rdoc/generators/xml_generator.rb
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
require 'ftools'
|
||||||
|
|
||||||
|
require 'rdoc/options'
|
||||||
|
require 'rdoc/markup/simple_markup'
|
||||||
|
require 'rdoc/markup/simple_markup/to_html'
|
||||||
|
require 'rdoc/generators/html_generator'
|
||||||
|
|
||||||
|
module Generators
|
||||||
|
|
||||||
|
# Generate XML output as one big file
|
||||||
|
|
||||||
|
class XMLGenerator < HTMLGenerator
|
||||||
|
|
||||||
|
# Standard generator factory
|
||||||
|
def XMLGenerator.for(options)
|
||||||
|
XMLGenerator.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 HtmlFile objects for each TopLevel object.
|
||||||
|
# * a list of HtmlClass 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 << HtmlFile.new(toplevel, @options, FILE_DIR)
|
||||||
|
end
|
||||||
|
|
||||||
|
RDoc::TopLevel.all_classes_and_modules.each do |cls|
|
||||||
|
build_class_list(cls, @files[0], CLASS_DIR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_class_list(from, html_file, class_dir)
|
||||||
|
@classes << HtmlClass.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
# this method is defined in the template file
|
||||||
|
write_extra_pages if defined? write_extra_pages
|
||||||
|
|
||||||
|
template = TemplatePage.new(RDoc::Page::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(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
|
||||||
|
|
||||||
|
end
|
16
lib/rdoc/markup/sample/rdoc2latex.rb
Normal file
16
lib/rdoc/markup/sample/rdoc2latex.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/local/bin/ruby
|
||||||
|
# Illustration of a script to convert an RDoc-style file to a LaTeX
|
||||||
|
# document
|
||||||
|
|
||||||
|
require 'rdoc/markup/simple_markup'
|
||||||
|
require 'rdoc/markup/simple_markup/to_latex'
|
||||||
|
|
||||||
|
p = SM::SimpleMarkup.new
|
||||||
|
h = SM::ToLaTeX.new
|
||||||
|
|
||||||
|
#puts "\\documentclass{report}"
|
||||||
|
#puts "\\usepackage{tabularx}"
|
||||||
|
#puts "\\usepackage{parskip}"
|
||||||
|
#puts "\\begin{document}"
|
||||||
|
puts p.convert(ARGF.read, h)
|
||||||
|
#puts "\\end{document}"
|
42
lib/rdoc/markup/sample/sample.rb
Normal file
42
lib/rdoc/markup/sample/sample.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# This program illustrates the basic use of the SimpleMarkup
|
||||||
|
# class. It extracts the first comment block from the
|
||||||
|
# simple_markup.rb file and converts it into HTML on
|
||||||
|
# standard output. Run it using
|
||||||
|
#
|
||||||
|
# % ruby sample.rb
|
||||||
|
#
|
||||||
|
# You should be in the sample/ directory when you do this,
|
||||||
|
# as it hardwires the path to the files it needs to require.
|
||||||
|
# This isn't necessary in the code you write once you've
|
||||||
|
# installed the package.
|
||||||
|
#
|
||||||
|
# For a better way of formatting code comment blocks (and more)
|
||||||
|
# see the rdoc package.
|
||||||
|
#
|
||||||
|
|
||||||
|
$:.unshift "../../.."
|
||||||
|
|
||||||
|
require 'rdoc/markup/simple_markup'
|
||||||
|
require 'rdoc/markup/simple_markup/to_html'
|
||||||
|
|
||||||
|
# Extract the comment block from the source file
|
||||||
|
|
||||||
|
input_string = ""
|
||||||
|
|
||||||
|
File.foreach("../simple_markup.rb") do |line|
|
||||||
|
break unless line.gsub!(/^\# ?/, '')
|
||||||
|
input_string << line
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a markup object
|
||||||
|
markup = SM::SimpleMarkup.new
|
||||||
|
|
||||||
|
# Attach it to an HTML formatter
|
||||||
|
h = SM::ToHtml.new
|
||||||
|
|
||||||
|
# And convert out comment block to html. Wrap it a body
|
||||||
|
# tag pair to let browsers view it
|
||||||
|
|
||||||
|
puts "<html><body>"
|
||||||
|
puts markup.convert(input_string, h)
|
||||||
|
puts "</body></html>"
|
477
lib/rdoc/markup/simple_markup.rb
Normal file
477
lib/rdoc/markup/simple_markup.rb
Normal file
|
@ -0,0 +1,477 @@
|
||||||
|
# = Introduction
|
||||||
|
#
|
||||||
|
# SimpleMarkup parses plain text documents and attempts to decompose
|
||||||
|
# them into their constituent parts. Some of these parts are high-level:
|
||||||
|
# paragraphs, chunks of verbatim text, list entries and the like. Other
|
||||||
|
# parts happen at the character level: a piece of bold text, a word in
|
||||||
|
# code font. This markup is similar in spirit to that used on WikiWiki
|
||||||
|
# webs, where folks create web pages using a simple set of formatting
|
||||||
|
# rules.
|
||||||
|
#
|
||||||
|
# SimpleMarkup itself does no output formatting: this is left to a
|
||||||
|
# different set of classes.
|
||||||
|
#
|
||||||
|
# SimpleMarkup is extendable at runtime: you can add new markup
|
||||||
|
# elements to be recognised in the documents that SimpleMarkup parses.
|
||||||
|
#
|
||||||
|
# SimpleMarkup is intended to be the basis for a family of tools which
|
||||||
|
# share the common requirement that simple, plain-text should be
|
||||||
|
# rendered in a variety of different output formats and media. It is
|
||||||
|
# envisaged that SimpleMarkup could be the basis for formating RDoc
|
||||||
|
# style comment blocks, Wiki entries, and online FAQs.
|
||||||
|
#
|
||||||
|
# = Basic Formatting
|
||||||
|
#
|
||||||
|
# * SimpleMarkup looks for a document's natural left margin. This is
|
||||||
|
# used as the initial margin for the document.
|
||||||
|
#
|
||||||
|
# * Consecutive lines starting at this margin are considered to be a
|
||||||
|
# paragraph.
|
||||||
|
#
|
||||||
|
# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
|
||||||
|
# taken to be the start of a list. The margin in increased to be the
|
||||||
|
# first non-space following the list start flag. Subsequent lines
|
||||||
|
# should be indented to this new margin until the list ends. For
|
||||||
|
# example:
|
||||||
|
#
|
||||||
|
# * this is a list with three paragraphs in
|
||||||
|
# the first item. This is the first paragraph.
|
||||||
|
#
|
||||||
|
# And this is the second paragraph.
|
||||||
|
#
|
||||||
|
# 1. This is an indented, numbered list.
|
||||||
|
# 2. This is the second item in that list
|
||||||
|
#
|
||||||
|
# This is the third conventional paragraph in the
|
||||||
|
# first list item.
|
||||||
|
#
|
||||||
|
# * This is the second item in the original list
|
||||||
|
#
|
||||||
|
# * You can also construct labeled lists, sometimes called description
|
||||||
|
# or definition lists. Do this by putting the label in square brackets
|
||||||
|
# and indenting the list body:
|
||||||
|
#
|
||||||
|
# [cat] a small furry mammal
|
||||||
|
# that seems to sleep a lot
|
||||||
|
#
|
||||||
|
# [ant] a little insect that is known
|
||||||
|
# to enjoy picnics
|
||||||
|
#
|
||||||
|
# A minor variation on labeled lists uses two colons to separate the
|
||||||
|
# label from the list body:
|
||||||
|
#
|
||||||
|
# cat:: a small furry mammal
|
||||||
|
# that seems to sleep a lot
|
||||||
|
#
|
||||||
|
# ant:: a little insect that is known
|
||||||
|
# to enjoy picnics
|
||||||
|
#
|
||||||
|
# This latter style guarantees that the list bodies' left margins are
|
||||||
|
# aligned: think of them as a two column table.
|
||||||
|
#
|
||||||
|
# * Any line that starts to the right of the current margin is treated
|
||||||
|
# as verbatim text. This is useful for code listings. The example of a
|
||||||
|
# list above is also verbatim text.
|
||||||
|
#
|
||||||
|
# * A line starting with an equals sign (=) is treated as a
|
||||||
|
# heading. Level one headings have one equals sign, level two headings
|
||||||
|
# have two,and so on.
|
||||||
|
#
|
||||||
|
# * A line starting with three or more hyphens (at the current indent)
|
||||||
|
# generates a horizontal rule. THe more hyphens, the thicker the rule
|
||||||
|
# (within reason, and if supported by the output device)
|
||||||
|
#
|
||||||
|
# * You can use markup within text (except verbatim) to change the
|
||||||
|
# appearance of parts of that text. Out of the box, SimpleMarkup
|
||||||
|
# supports word-based and general markup.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# 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 _emphasized_ font
|
||||||
|
# [\<tt>text...</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
|
||||||
|
# preceding the first character with a backslash, so \\\<b>bold
|
||||||
|
# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold
|
||||||
|
# respectively.
|
||||||
|
#
|
||||||
|
# = Using SimpleMarkup
|
||||||
|
#
|
||||||
|
# For information on using SimpleMarkup programatically,
|
||||||
|
# see SM::SimpleMarkup.
|
||||||
|
#
|
||||||
|
# Author:: Dave Thomas, dave@pragmaticprogrammer.com
|
||||||
|
# Version:: 0.0
|
||||||
|
# License:: Ruby license
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
require 'rdoc/markup/simple_markup/fragments'
|
||||||
|
require 'rdoc/markup/simple_markup/lines.rb'
|
||||||
|
|
||||||
|
module SM #:nodoc:
|
||||||
|
|
||||||
|
# == Synopsis
|
||||||
|
#
|
||||||
|
# This code converts <tt>input_string</tt>, which is in the format
|
||||||
|
# described in markup/simple_markup.rb, to HTML. The conversion
|
||||||
|
# takes place in the +convert+ method, so you can use the same
|
||||||
|
# SimpleMarkup object to convert multiple input strings.
|
||||||
|
#
|
||||||
|
# require 'rdoc/markup/simple_markup'
|
||||||
|
# require 'rdoc/markup/simple_markup/to_html'
|
||||||
|
#
|
||||||
|
# p = SM::SimpleMarkup.new
|
||||||
|
# h = SM::ToHtml.new
|
||||||
|
#
|
||||||
|
# puts p.convert(input_string, h)
|
||||||
|
#
|
||||||
|
# You can extend the SimpleMarkup parser to recognise new markup
|
||||||
|
# sequences, and to add special processing for text that matches a
|
||||||
|
# regular epxression. Here we make WikiWords significant to the parser,
|
||||||
|
# and also make the sequences {word} and \<no>text...</no> signify
|
||||||
|
# strike-through text. When then subclass the HTML output class to deal
|
||||||
|
# with these:
|
||||||
|
#
|
||||||
|
# require 'rdoc/markup/simple_markup'
|
||||||
|
# require 'rdoc/markup/simple_markup/to_html'
|
||||||
|
#
|
||||||
|
# class WikiHtml < SM::ToHtml
|
||||||
|
# def handle_special_WIKIWORD(special)
|
||||||
|
# "<font color=red>" + special.text + "</font>"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# p = SM::SimpleMarkup.new
|
||||||
|
# p.add_word_pair("{", "}", :STRIKE)
|
||||||
|
# p.add_html("no", :STRIKE)
|
||||||
|
#
|
||||||
|
# p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
|
||||||
|
#
|
||||||
|
# h = WikiHtml.new
|
||||||
|
# h.add_tag(:STRIKE, "<strike>", "</strike>")
|
||||||
|
#
|
||||||
|
# puts "<body>" + p.convert(ARGF.read, h) + "</body>"
|
||||||
|
#
|
||||||
|
# == Output Formatters
|
||||||
|
#
|
||||||
|
# _missing_
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
class SimpleMarkup
|
||||||
|
|
||||||
|
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|$)
|
||||||
|
\s*
|
||||||
|
)/x
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# take a block of text and use various heuristics to determine
|
||||||
|
# it's structure (paragraphs, lists, and so on). Invoke an
|
||||||
|
# event handler as we identify significant chunks.
|
||||||
|
#
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@am = AttributeManager.new
|
||||||
|
@output = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add to the sequences used to add formatting to an individual word
|
||||||
|
# (such as *bold*). Matching entries will generate attibutes
|
||||||
|
# that the output formatters can recognize by their +name+
|
||||||
|
|
||||||
|
def add_word_pair(start, stop, name)
|
||||||
|
@am.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)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add to other inline sequences. For example, we could add
|
||||||
|
# WikiWords using something like:
|
||||||
|
#
|
||||||
|
# parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
|
||||||
|
#
|
||||||
|
# Each wiki word will be presented to the output formatter
|
||||||
|
# via the accept_special method
|
||||||
|
#
|
||||||
|
|
||||||
|
def add_special(pattern, name)
|
||||||
|
@am.add_special(pattern, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# We take a string, split it into lines, work out the type of
|
||||||
|
# each line, and from there deduce groups of lines (for example
|
||||||
|
# all lines in a paragraph). We then invoke the output formatter
|
||||||
|
# using a Visitor to display the result
|
||||||
|
|
||||||
|
def convert(str, op)
|
||||||
|
@lines = Lines.new(str.split(/\r?\n/).collect { |aLine|
|
||||||
|
Line.new(aLine) })
|
||||||
|
return "" if @lines.empty?
|
||||||
|
@lines.normalize
|
||||||
|
assign_types_to_lines
|
||||||
|
group = group_lines
|
||||||
|
# call the output formatter to handle the result
|
||||||
|
# group.to_a.each {|i| p i}
|
||||||
|
group.accept(@am, 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.isBlank?
|
||||||
|
line.stamp(Line::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(Line::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 ListBase::BULLET
|
||||||
|
when /^\d/ then ListBase::NUMBER
|
||||||
|
when /^[A-Z]/ then ListBase::UPPERALPHA
|
||||||
|
when /^[a-z]/ then ListBase::LOWERALPHA
|
||||||
|
else raise "Invalid List Type: #{self.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
line.stamp(Line::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(Line::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(Line::VERBATIM, level)
|
||||||
|
else
|
||||||
|
line.stamp(Line::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 ab 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 /^\[/
|
||||||
|
flag = ListBase::LABELED
|
||||||
|
prefix = prefix[1, prefix.length-2]
|
||||||
|
when /:$/
|
||||||
|
flag = ListBase::NOTE
|
||||||
|
prefix.chop!
|
||||||
|
else raise "Invalid List Type: #{self.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# body is on the next line
|
||||||
|
|
||||||
|
if text.length <= offset
|
||||||
|
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
|
||||||
|
@lines.unget
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
offset = i
|
||||||
|
prefix_length = 0
|
||||||
|
@lines.delete(original_line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
line.stamp(Line::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
|
||||||
|
|
||||||
|
inList = false
|
||||||
|
wantedType = wantedLevel = nil
|
||||||
|
|
||||||
|
block = LineCollection.new
|
||||||
|
group = nil
|
||||||
|
|
||||||
|
while line = @lines.next
|
||||||
|
if line.level == wantedLevel and line.type == wantedType
|
||||||
|
group.add_text(line.text)
|
||||||
|
else
|
||||||
|
group = block.fragment_for(line)
|
||||||
|
block.add(group)
|
||||||
|
if line.type == Line::LIST
|
||||||
|
wantedType = Line::PARAGRAPH
|
||||||
|
else
|
||||||
|
wantedType = line.type
|
||||||
|
end
|
||||||
|
wantedLevel = 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
|
||||||
|
|
||||||
|
end
|
328
lib/rdoc/markup/simple_markup/fragments.rb
Normal file
328
lib/rdoc/markup/simple_markup/fragments.rb
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
require 'rdoc/markup/simple_markup/lines.rb'
|
||||||
|
require 'rdoc/markup/simple_markup/inline.rb'
|
||||||
|
|
||||||
|
module SM
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
######
|
||||||
|
# This is a simple factory system that lets us associate fragement
|
||||||
|
# types (a string) with a subclass of fragment
|
||||||
|
|
||||||
|
TYPE_MAP = {}
|
||||||
|
|
||||||
|
def Fragment.type_name(name)
|
||||||
|
TYPE_MAP[name] = self
|
||||||
|
end
|
||||||
|
|
||||||
|
def Fragment.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
|
||||||
|
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 Line::PARAGRAPH
|
||||||
|
end
|
||||||
|
|
||||||
|
class BlankLine < Paragraph
|
||||||
|
type_name Line::BLANK
|
||||||
|
end
|
||||||
|
|
||||||
|
class Heading < Paragraph
|
||||||
|
type_name Line::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 = :BULLET
|
||||||
|
NUMBER = :NUMBER
|
||||||
|
UPPERALPHA = :UPPERALPHA
|
||||||
|
LOWERALPHA = :LOWERALPHA
|
||||||
|
LABELED = :LABELED
|
||||||
|
NOTE = :NOTE
|
||||||
|
end
|
||||||
|
|
||||||
|
class ListItem < ListBase
|
||||||
|
type_name Line::LIST
|
||||||
|
|
||||||
|
# def label
|
||||||
|
# am = AttributeManager.new(@param)
|
||||||
|
# am.flow
|
||||||
|
# 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 Line::VERBATIM
|
||||||
|
|
||||||
|
def add_text(txt)
|
||||||
|
@txt << txt.chomp << "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A horizontal rule
|
||||||
|
class Rule < Fragment
|
||||||
|
type_name Line::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
|
||||||
|
|
||||||
|
# For testing
|
||||||
|
def to_a
|
||||||
|
@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
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# now insert 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
|
||||||
|
|
||||||
|
# Finally 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 @fragments[i].kind_of?(BlankLine) and
|
||||||
|
@fragments[i+1].kind_of?(ListEnd)
|
||||||
|
@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
|
348
lib/rdoc/markup/simple_markup/inline.rb
Normal file
348
lib/rdoc/markup/simple_markup/inline.rb
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
module SM
|
||||||
|
|
||||||
|
# We manage a set of attributes. Each attribute has a symbol name
|
||||||
|
# and a bit value
|
||||||
|
|
||||||
|
class Attribute
|
||||||
|
SPECIAL = 1
|
||||||
|
|
||||||
|
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
|
||||||
|
@@next_bitmap = 2
|
||||||
|
|
||||||
|
def Attribute.bitmap_for(name)
|
||||||
|
bitmap = @@name_to_bitmap[name]
|
||||||
|
if !bitmap
|
||||||
|
bitmap = @@next_bitmap
|
||||||
|
@@next_bitmap <<= 1
|
||||||
|
@@name_to_bitmap[name] = bitmap
|
||||||
|
end
|
||||||
|
bitmap
|
||||||
|
end
|
||||||
|
|
||||||
|
def Attribute.as_string(bitmap)
|
||||||
|
return "none" if bitmap.zero?
|
||||||
|
res = []
|
||||||
|
@@name_to_bitmap.each do |name, bit|
|
||||||
|
res << name if (bitmap & bit) != 0
|
||||||
|
end
|
||||||
|
res.join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def Attribute.each_name_of(bitmap)
|
||||||
|
@@name_to_bitmap.each do |name, bit|
|
||||||
|
next if bit == SPECIAL
|
||||||
|
yield name.to_s if (bitmap & bit) != 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# An AttrChanger records a change in attributes. It contains
|
||||||
|
# a bitmap of the attributes to turn on, and a bitmap of those to
|
||||||
|
# turn off
|
||||||
|
|
||||||
|
AttrChanger = Struct.new(:turn_on, :turn_off)
|
||||||
|
class AttrChanger
|
||||||
|
def to_s
|
||||||
|
"Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# An array of attributes which parallels the characters in a string
|
||||||
|
class AttrSpan
|
||||||
|
def initialize(length)
|
||||||
|
@attrs = Array.new(length, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_attrs(start, length, bits)
|
||||||
|
for i in start ... (start+length)
|
||||||
|
@attrs[i] |= bits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](n)
|
||||||
|
@attrs[n]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Hold details of a special sequence
|
||||||
|
|
||||||
|
class Special
|
||||||
|
attr_reader :type
|
||||||
|
attr_accessor :text
|
||||||
|
|
||||||
|
def initialize(type, text)
|
||||||
|
@type, @text = type, text
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(o)
|
||||||
|
self.text == o.text && self.type == o.type
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"Special: type=#{type}, text=#{text.dump}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AttributeManager
|
||||||
|
|
||||||
|
NULL = "\000".freeze
|
||||||
|
|
||||||
|
##
|
||||||
|
# We work by substituting non-printing characters in to the
|
||||||
|
# text. For now I'm assuming that I can substitute
|
||||||
|
# a character in the range 0..8 for a 7 bit character
|
||||||
|
# without damaging the encoded string, but this might
|
||||||
|
# be optimistic
|
||||||
|
#
|
||||||
|
|
||||||
|
=begin
|
||||||
|
ATTR_FLAG = 001
|
||||||
|
A_START = 002
|
||||||
|
A_END = 003
|
||||||
|
A_SPECIAL_START = 005
|
||||||
|
A_SPECIAL_END = 006
|
||||||
|
|
||||||
|
START_ATTR = ATTR_FLAG.chr + A_START.chr
|
||||||
|
END_ATTR = ATTR_FLAG.chr + A_END.chr
|
||||||
|
|
||||||
|
START_SPECIAL = ATTR_FLAG.chr + A_SPECIAL_START.chr
|
||||||
|
END_SPECIAL = ATTR_FLAG.chr + A_SPECIAL_END.chr
|
||||||
|
|
||||||
|
=end
|
||||||
|
A_PROTECT = 004
|
||||||
|
PROTECT_ATTR = A_PROTECT.chr
|
||||||
|
|
||||||
|
# This maps delimiters that occur around words (such as
|
||||||
|
# *bold* or +tt+) where the start and end delimiters
|
||||||
|
# and the same. This lets us optimize the regexp
|
||||||
|
MATCHING_WORD_PAIRS = {}
|
||||||
|
|
||||||
|
# And this is used when the delimiters aren't the same. In this
|
||||||
|
# case the hash maps a pattern to the attribute character
|
||||||
|
WORD_PAIR_MAP = {}
|
||||||
|
|
||||||
|
# This maps HTML tags to the corresponding attribute char
|
||||||
|
HTML_TAGS = {}
|
||||||
|
|
||||||
|
# And this maps _special_ sequences to a name. A special sequence
|
||||||
|
# is something like a WikiWord
|
||||||
|
SPECIAL = {}
|
||||||
|
|
||||||
|
# Return an attribute object with the given turn_on
|
||||||
|
# and turn_off bits set
|
||||||
|
|
||||||
|
def attribute(turn_on, turn_off)
|
||||||
|
AttrChanger.new(turn_on, turn_off)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def change_attribute(current, new)
|
||||||
|
diff = current ^ new
|
||||||
|
attribute(new & diff, current & diff)
|
||||||
|
end
|
||||||
|
|
||||||
|
def changed_attribute_by_name(current_set, new_set)
|
||||||
|
current = new = 0
|
||||||
|
current_set.each {|name| current |= Attribute.bitmap_for(name) }
|
||||||
|
new_set.each {|name| new |= Attribute.bitmap_for(name) }
|
||||||
|
change_attribute(current, new)
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy_string(start_pos, end_pos)
|
||||||
|
res = @str[start_pos...end_pos]
|
||||||
|
res.gsub!(/\000/, '')
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
# Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
|
||||||
|
# where <char> is a per-attribute specific character
|
||||||
|
|
||||||
|
def convert_attrs(str, attrs)
|
||||||
|
# first do matching ones
|
||||||
|
tags = MATCHING_WORD_PAIRS.keys.join("")
|
||||||
|
re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
|
||||||
|
# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
|
||||||
|
1 while str.gsub!(Regexp.new(re)) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# then non-matching
|
||||||
|
unless WORD_PAIR_MAP.empty?
|
||||||
|
WORD_PAIR_MAP.each do |regexp, attr|
|
||||||
|
str.gsub!(regexp) {
|
||||||
|
attrs.set_attrs($`.length + $1.length, $2.length, attr)
|
||||||
|
NULL*$1.length + $2 + NULL*$3.length
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_html(str, attrs)
|
||||||
|
tags = HTML_TAGS.keys.join("|")
|
||||||
|
re = "<(#{tags})>(.*?)</\\1>"
|
||||||
|
1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
|
||||||
|
attr = HTML_TAGS[$1.downcase]
|
||||||
|
html_length = $1.length + 2
|
||||||
|
seq = NULL * html_length
|
||||||
|
attrs.set_attrs($`.length + html_length, $2.length, attr)
|
||||||
|
seq + $2 + seq + NULL
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_specials(str, attrs)
|
||||||
|
unless SPECIAL.empty?
|
||||||
|
SPECIAL.each do |regexp, attr|
|
||||||
|
str.scan(regexp) do
|
||||||
|
attrs.set_attrs($`.length, $1.length, attr | Attribute::SPECIAL)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A \ in front of a character that would normally be
|
||||||
|
# processed turns off processing. We do this by turning
|
||||||
|
# \< into <#{PROTECT}
|
||||||
|
|
||||||
|
PROTECTABLE = [ "<" << "\\" ] #"
|
||||||
|
|
||||||
|
|
||||||
|
def mask_protected_sequences
|
||||||
|
protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
|
||||||
|
@str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def unmask_protected_sequences
|
||||||
|
@str.gsub!(/(.)#{PROTECT_ATTR}/, '\1')
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_word_pair(start, stop, name)
|
||||||
|
raise "Word flags may not start '<'" if start[0] == ?<
|
||||||
|
bitmap = Attribute.bitmap_for(name)
|
||||||
|
if start == stop
|
||||||
|
MATCHING_WORD_PAIRS[start] = bitmap
|
||||||
|
else
|
||||||
|
pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
|
||||||
|
# "([A-Za-z]+)" +
|
||||||
|
"(\\S+)" +
|
||||||
|
"(" + Regexp.escape(stop) +")")
|
||||||
|
WORD_PAIR_MAP[pattern] = bitmap
|
||||||
|
end
|
||||||
|
PROTECTABLE << start[0,1]
|
||||||
|
PROTECTABLE.uniq!
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_html(tag, name)
|
||||||
|
HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_special(pattern, name)
|
||||||
|
SPECIAL[pattern] = Attribute.bitmap_for(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def flow(str)
|
||||||
|
@str = str
|
||||||
|
@attrs = AttrSpan.new(str.length)
|
||||||
|
|
||||||
|
puts("Before flow, str='#{@str.dump}'") if $DEBUG
|
||||||
|
mask_protected_sequences
|
||||||
|
convert_attrs(@str, @attrs)
|
||||||
|
convert_html(@str, @attrs)
|
||||||
|
convert_specials(str, @attrs)
|
||||||
|
unmask_protected_sequences
|
||||||
|
puts("After flow, str='#{@str.dump}'") if $DEBUG
|
||||||
|
return split_into_flow
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_attributes
|
||||||
|
puts
|
||||||
|
puts @str.tr(NULL, "!")
|
||||||
|
bit = 1
|
||||||
|
16.times do |bno|
|
||||||
|
line = ""
|
||||||
|
@str.length.times do |i|
|
||||||
|
if (@attrs[i] & bit) == 0
|
||||||
|
line << " "
|
||||||
|
else
|
||||||
|
if bno.zero?
|
||||||
|
line << "S"
|
||||||
|
else
|
||||||
|
line << ("%d" % (bno+1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
puts(line) unless line =~ /^ *$/
|
||||||
|
bit <<= 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def split_into_flow
|
||||||
|
|
||||||
|
display_attributes if $DEBUG
|
||||||
|
|
||||||
|
res = []
|
||||||
|
current_attr = 0
|
||||||
|
str = ""
|
||||||
|
|
||||||
|
|
||||||
|
str_len = @str.length
|
||||||
|
|
||||||
|
# skip leading invisible text
|
||||||
|
i = 0
|
||||||
|
i += 1 while i < str_len and @str[i].zero?
|
||||||
|
start_pos = i
|
||||||
|
|
||||||
|
# then scan the string, chunking it on attribute changes
|
||||||
|
while i < str_len
|
||||||
|
new_attr = @attrs[i]
|
||||||
|
if new_attr != current_attr
|
||||||
|
if i > start_pos
|
||||||
|
res << copy_string(start_pos, i)
|
||||||
|
start_pos = i
|
||||||
|
end
|
||||||
|
|
||||||
|
res << change_attribute(current_attr, new_attr)
|
||||||
|
current_attr = new_attr
|
||||||
|
|
||||||
|
if (current_attr & Attribute::SPECIAL) != 0
|
||||||
|
i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
|
||||||
|
res << Special.new(current_attr, copy_string(start_pos, i))
|
||||||
|
start_pos = i
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# move on, skipping any invisible characters
|
||||||
|
begin
|
||||||
|
i += 1
|
||||||
|
end while i < str_len and @str[i].zero?
|
||||||
|
end
|
||||||
|
|
||||||
|
# tidy up trailing text
|
||||||
|
if start_pos < str_len
|
||||||
|
res << copy_string(start_pos, str_len)
|
||||||
|
end
|
||||||
|
|
||||||
|
# and reset to all attributes off
|
||||||
|
res << change_attribute(current_attr, 0) if current_attr != 0
|
||||||
|
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
151
lib/rdoc/markup/simple_markup/lines.rb
Normal file
151
lib/rdoc/markup/simple_markup/lines.rb
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
module SM
|
||||||
|
|
||||||
|
class Line
|
||||||
|
INFINITY = 9999
|
||||||
|
|
||||||
|
BLANK = :BLANK
|
||||||
|
HEADING = :HEADING
|
||||||
|
LIST = :LIST
|
||||||
|
RULE = :RULE
|
||||||
|
PARAGRAPH = :PARAGRAPH
|
||||||
|
VERBATIM = :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 isBlank?
|
||||||
|
@text.length.zero?
|
||||||
|
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 # for debugging
|
||||||
|
|
||||||
|
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 == Line::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
|
68
lib/rdoc/markup/simple_markup/preprocess.rb
Normal file
68
lib/rdoc/markup/simple_markup/preprocess.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
module SM
|
||||||
|
|
||||||
|
##
|
||||||
|
# Handle common directives that can occur in a block of text:
|
||||||
|
#
|
||||||
|
# : include : filename
|
||||||
|
#
|
||||||
|
|
||||||
|
class PreProcess
|
||||||
|
|
||||||
|
def initialize(input_file_name, include_path)
|
||||||
|
@input_file_name = input_file_name
|
||||||
|
@include_path = include_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look for common options in a chunk of text. Options that
|
||||||
|
# we don't handle are passed back to our caller
|
||||||
|
# as |directive, param|
|
||||||
|
|
||||||
|
def handle(text)
|
||||||
|
text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
|
||||||
|
|
||||||
|
directive = $2.downcase
|
||||||
|
param = $3
|
||||||
|
|
||||||
|
case directive
|
||||||
|
|
||||||
|
when "include"
|
||||||
|
include_file($3, $1)
|
||||||
|
|
||||||
|
else
|
||||||
|
yield(directive, param)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#######
|
||||||
|
private
|
||||||
|
#######
|
||||||
|
|
||||||
|
# Include a file, indenting it correctly
|
||||||
|
|
||||||
|
def include_file(name, indent)
|
||||||
|
if (full_name = find_include_file(name))
|
||||||
|
content = File.open(full_name) {|f| f.read}
|
||||||
|
res = content.gsub(/^#?/, indent)
|
||||||
|
else
|
||||||
|
$stderr.puts "Couldn't find file to include: '#{name}'"
|
||||||
|
''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look for the given file in the directory containing the current
|
||||||
|
# file, and then in each of the directories specified in the
|
||||||
|
# RDOC_INCLUDE path
|
||||||
|
|
||||||
|
def find_include_file(name)
|
||||||
|
to_search = [ File.dirname(@input_file_name) ].concat @include_path
|
||||||
|
to_search.each do |dir|
|
||||||
|
full_name = File.join(dir, name)
|
||||||
|
stat = File.stat(full_name) rescue next
|
||||||
|
return full_name if stat.readable?
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
289
lib/rdoc/markup/simple_markup/to_html.rb
Normal file
289
lib/rdoc/markup/simple_markup/to_html.rb
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
require 'rdoc/markup/simple_markup/fragments'
|
||||||
|
require 'rdoc/markup/simple_markup/inline'
|
||||||
|
|
||||||
|
require 'cgi'
|
||||||
|
|
||||||
|
module SM
|
||||||
|
|
||||||
|
class ToHtml
|
||||||
|
|
||||||
|
LIST_TYPE_TO_HTML = {
|
||||||
|
ListBase::BULLET => [ "<ul>", "</ul>" ],
|
||||||
|
ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
||||||
|
ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
||||||
|
ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
||||||
|
ListBase::LABELED => [ "<dl>", "</dl>" ],
|
||||||
|
ListBase::NOTE => [ "<table>", "</table>" ],
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineTag = Struct.new(:bit, :on, :off)
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
init_tags
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set up the standard mapping of attributes to HTML tags
|
||||||
|
#
|
||||||
|
def init_tags
|
||||||
|
@attr_tags = [
|
||||||
|
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
|
||||||
|
InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
|
||||||
|
InlineTag.new(SM::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(SM::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 = ""
|
||||||
|
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 << 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
|
||||||
|
|
||||||
|
# some of these patterns are taken from SmartyPants...
|
||||||
|
|
||||||
|
def convert_string(item)
|
||||||
|
CGI.escapeHTML(item).
|
||||||
|
|
||||||
|
|
||||||
|
# convert -- to em-dash, (-- to en-dash)
|
||||||
|
gsub(/---?/, '—'). #gsub(/--/, '–').
|
||||||
|
|
||||||
|
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
|
||||||
|
gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…').
|
||||||
|
|
||||||
|
# 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\)/, '©').
|
||||||
|
|
||||||
|
# convert and registered trademark
|
||||||
|
gsub(/\(r\)/, '®')
|
||||||
|
|
||||||
|
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 =
|
||||||
|
annotate("<h#{level}>") +
|
||||||
|
convert_flow(flow) +
|
||||||
|
annotate("</h#{level}>\n")
|
||||||
|
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])
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_item_start(am, fragment)
|
||||||
|
case fragment.type
|
||||||
|
when ListBase::BULLET, ListBase::NUMBER
|
||||||
|
annotate("<li>")
|
||||||
|
|
||||||
|
when ListBase::UPPERALPHA
|
||||||
|
annotate("<li type=\"A\">")
|
||||||
|
|
||||||
|
when ListBase::LOWERALPHA
|
||||||
|
annotate("<li type=\"a\">")
|
||||||
|
|
||||||
|
when ListBase::LABELED
|
||||||
|
annotate("<dt>") +
|
||||||
|
convert_flow(am.flow(fragment.param)) +
|
||||||
|
annotate("</dt>") +
|
||||||
|
annotate("<dd>")
|
||||||
|
|
||||||
|
when ListBase::NOTE
|
||||||
|
annotate("<tr>") +
|
||||||
|
annotate("<td valign=\"top\">") +
|
||||||
|
convert_flow(am.flow(fragment.param)) +
|
||||||
|
annotate("</td>") +
|
||||||
|
annotate("<td>")
|
||||||
|
else
|
||||||
|
raise "Invalid list type"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_end_for(fragment_type)
|
||||||
|
case fragment_type
|
||||||
|
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
|
||||||
|
"</li>"
|
||||||
|
when ListBase::LABELED
|
||||||
|
"</dd>"
|
||||||
|
when ListBase::NOTE
|
||||||
|
"</td></tr>"
|
||||||
|
else
|
||||||
|
raise "Invalid list type"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
333
lib/rdoc/markup/simple_markup/to_latex.rb
Normal file
333
lib/rdoc/markup/simple_markup/to_latex.rb
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
require 'rdoc/markup/simple_markup/fragments'
|
||||||
|
require 'rdoc/markup/simple_markup/inline'
|
||||||
|
|
||||||
|
require 'cgi'
|
||||||
|
|
||||||
|
module SM
|
||||||
|
|
||||||
|
# Convert SimpleMarkup to basic LaTeX report format
|
||||||
|
|
||||||
|
class ToLaTeX
|
||||||
|
|
||||||
|
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)
|
||||||
|
SM::ToLaTeX.l(arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
LIST_TYPE_TO_LATEX = {
|
||||||
|
ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
|
||||||
|
ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
|
||||||
|
ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
|
||||||
|
ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
|
||||||
|
ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
|
||||||
|
ListBase::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(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
|
||||||
|
InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
|
||||||
|
InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Escape a LaTeX string
|
||||||
|
def escape(str)
|
||||||
|
# $stderr.print "FE: ", str
|
||||||
|
s = str.
|
||||||
|
# sub(/\s+$/, '').
|
||||||
|
gsub(/([_\${}&%#])/, "#{BS}\\1").
|
||||||
|
gsub(/\\/, BACKSLASH).
|
||||||
|
gsub(/\^/, HAT).
|
||||||
|
gsub(/~/, TILDE).
|
||||||
|
gsub(/</, LESSTHAN).
|
||||||
|
gsub(/>/, GREATERTHAN).
|
||||||
|
gsub(/,,/, ",{},").
|
||||||
|
gsub(/\`/, BACKQUOTE)
|
||||||
|
# $stderr.print "-> ", s, "\n"
|
||||||
|
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(SM::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}'"
|
||||||
|
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 ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
|
||||||
|
"\\item "
|
||||||
|
|
||||||
|
when ListBase::LABELED
|
||||||
|
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
|
||||||
|
|
||||||
|
when ListBase::NOTE
|
||||||
|
convert_flow(am.flow(fragment.param)) + " & "
|
||||||
|
else
|
||||||
|
raise "Invalid list type"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_end_for(fragment_type)
|
||||||
|
case fragment_type
|
||||||
|
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA, ListBase::LABELED
|
||||||
|
""
|
||||||
|
when ListBase::NOTE
|
||||||
|
"\\\\\n"
|
||||||
|
else
|
||||||
|
raise "Invalid list type"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
2
lib/rdoc/markup/test/AllTests.rb
Normal file
2
lib/rdoc/markup/test/AllTests.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
require 'TestParse.rb'
|
||||||
|
require 'TestInline.rb'
|
151
lib/rdoc/markup/test/TestInline.rb
Normal file
151
lib/rdoc/markup/test/TestInline.rb
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
require "test/unit"
|
||||||
|
|
||||||
|
$:.unshift "../../.."
|
||||||
|
|
||||||
|
require "rdoc/markup/simple_markup/inline"
|
||||||
|
|
||||||
|
class TestInline < Test::Unit::TestCase
|
||||||
|
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@am = SM::AttributeManager.new
|
||||||
|
|
||||||
|
@bold_on = @am.changed_attribute_by_name([], [:BOLD])
|
||||||
|
@bold_off = @am.changed_attribute_by_name([:BOLD], [])
|
||||||
|
|
||||||
|
@tt_on = @am.changed_attribute_by_name([], [:TT])
|
||||||
|
@tt_off = @am.changed_attribute_by_name([:TT], [])
|
||||||
|
|
||||||
|
@em_on = @am.changed_attribute_by_name([], [:EM])
|
||||||
|
@em_off = @am.changed_attribute_by_name([:EM], [])
|
||||||
|
|
||||||
|
@bold_em_on = @am.changed_attribute_by_name([], [:BOLD] | [:EM])
|
||||||
|
@bold_em_off = @am.changed_attribute_by_name([:BOLD] | [:EM], [])
|
||||||
|
|
||||||
|
@em_then_bold = @am.changed_attribute_by_name([:EM], [:EM] | [:BOLD])
|
||||||
|
|
||||||
|
@em_to_bold = @am.changed_attribute_by_name([:EM], [:BOLD])
|
||||||
|
|
||||||
|
@am.add_word_pair("{", "}", :WOMBAT)
|
||||||
|
@wombat_on = @am.changed_attribute_by_name([], [:WOMBAT])
|
||||||
|
@wombat_off = @am.changed_attribute_by_name([:WOMBAT], [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def crossref(text)
|
||||||
|
[ @am.changed_attribute_by_name([], [:CROSSREF] | [:_SPECIAL_]),
|
||||||
|
SM::Special.new(33, text),
|
||||||
|
@am.changed_attribute_by_name([:CROSSREF] | [:_SPECIAL_], [])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_special
|
||||||
|
# class names, variable names, file names, or instance variables
|
||||||
|
@am.add_special(/(
|
||||||
|
\b([A-Z]\w+(::\w+)*)
|
||||||
|
| \#\w+[!?=]?
|
||||||
|
| \b\w+([_\/\.]+\w+)+[!?=]?
|
||||||
|
)/x,
|
||||||
|
:CROSSREF)
|
||||||
|
|
||||||
|
assert_equal(["cat"], @am.flow("cat"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", crossref("#fred"), " dog"].flatten,
|
||||||
|
@am.flow("cat #fred dog"))
|
||||||
|
|
||||||
|
assert_equal([crossref("#fred"), " dog"].flatten,
|
||||||
|
@am.flow("#fred dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", crossref("#fred")].flatten, @am.flow("cat #fred"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_basic
|
||||||
|
assert_equal(["cat"], @am.flow("cat"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @bold_on, "and", @bold_off, " dog"],
|
||||||
|
@am.flow("cat *and* dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @bold_on, "AND", @bold_off, " dog"],
|
||||||
|
@am.flow("cat *AND* dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "And", @em_off, " dog"],
|
||||||
|
@am.flow("cat _And_ dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat *and dog*"], @am.flow("cat *and dog*"))
|
||||||
|
|
||||||
|
assert_equal(["*cat and* dog"], @am.flow("*cat and* dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat *and ", @bold_on, "dog", @bold_off],
|
||||||
|
@am.flow("cat *and *dog*"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and", @em_off, " dog"],
|
||||||
|
@am.flow("cat _and_ dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat_and_dog"],
|
||||||
|
@am.flow("cat_and_dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @tt_on, "and", @tt_off, " dog"],
|
||||||
|
@am.flow("cat +and+ dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @bold_on, "a_b_c", @bold_off, " dog"],
|
||||||
|
@am.flow("cat *a_b_c* dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat __ dog"],
|
||||||
|
@am.flow("cat __ dog"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "_", @em_off, " dog"],
|
||||||
|
@am.flow("cat ___ dog"))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_combined
|
||||||
|
assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
|
||||||
|
@am.flow("cat _and_ *dog*"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "a__nd", @em_off, " ", @bold_on, "dog", @bold_off],
|
||||||
|
@am.flow("cat _a__nd_ *dog*"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_html_like
|
||||||
|
assert_equal(["cat ", @tt_on, "dog", @tt_off], @am.flow("cat <tt>dog</Tt>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
|
||||||
|
@am.flow("cat <i>and</i> <B>dog</b>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
|
||||||
|
@am.flow("cat <i>and <B>dog</B></I>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
|
||||||
|
@am.flow("cat <i>and </i><b>dog</b>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
|
||||||
|
@am.flow("cat <i>and <b></i>dog</b>"))
|
||||||
|
|
||||||
|
assert_equal([@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
|
||||||
|
@am.flow("<tt>cat</tt> <i>and <b></i>dog</b>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
|
||||||
|
@am.flow("cat <i>and <b>dog</b></i>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
|
||||||
|
@am.flow("cat <i><b>and</b></i> dog"))
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_protect
|
||||||
|
assert_equal(['cat \\ dog'], @am.flow('cat \\ dog'))
|
||||||
|
|
||||||
|
assert_equal(["cat <tt>dog</Tt>"], @am.flow("cat \\<tt>dog</Tt>"))
|
||||||
|
|
||||||
|
assert_equal(["cat ", @em_on, "and", @em_off, " <B>dog</b>"],
|
||||||
|
@am.flow("cat <i>and</i> \\<B>dog</b>"))
|
||||||
|
|
||||||
|
assert_equal(["*word* or <b>text</b>"], @am.flow("\\*word* or \\<b>text</b>"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_adding
|
||||||
|
assert_equal(["cat ", @wombat_on, "and", @wombat_off, " dog" ],
|
||||||
|
@am.flow("cat {and} dog"))
|
||||||
|
# assert_equal(["cat {and} dog" ], @am.flow("cat \\{and} dog"))
|
||||||
|
end
|
||||||
|
end
|
503
lib/rdoc/markup/test/TestParse.rb
Normal file
503
lib/rdoc/markup/test/TestParse.rb
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
require 'test/unit'
|
||||||
|
|
||||||
|
$:.unshift "../../.."
|
||||||
|
|
||||||
|
require 'rdoc/markup/simple_markup'
|
||||||
|
|
||||||
|
include SM
|
||||||
|
|
||||||
|
class TestParse < Test::Unit::TestCase
|
||||||
|
|
||||||
|
class MockOutput
|
||||||
|
def start_accepting
|
||||||
|
@res = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def end_accepting
|
||||||
|
@res
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_paragraph(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_verbatim(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_list_start(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_list_end(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_list_item(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_blank_line(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_heading(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def accept_rule(am, fragment)
|
||||||
|
@res << fragment.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def basic_conv(str)
|
||||||
|
sm = SimpleMarkup.new
|
||||||
|
mock = MockOutput.new
|
||||||
|
sm.convert(str, mock)
|
||||||
|
sm.content
|
||||||
|
end
|
||||||
|
|
||||||
|
def line_types(str, expected)
|
||||||
|
p = SimpleMarkup.new
|
||||||
|
mock = MockOutput.new
|
||||||
|
p.convert(str, mock)
|
||||||
|
assert_equal(expected, p.get_line_types.map{|type| type.to_s[0,1]}.join(''))
|
||||||
|
end
|
||||||
|
|
||||||
|
def line_groups(str, expected)
|
||||||
|
p = SimpleMarkup.new
|
||||||
|
mock = MockOutput.new
|
||||||
|
|
||||||
|
block = p.convert(str, mock)
|
||||||
|
|
||||||
|
if block != expected
|
||||||
|
rows = (0...([expected.size, block.size].max)).collect{|i|
|
||||||
|
[expected[i]||"nil", block[i]||"nil"]
|
||||||
|
}
|
||||||
|
printf "\n\n%35s %35s\n", "Expected", "Got"
|
||||||
|
rows.each {|e,g| printf "%35s %35s\n", e.dump, g.dump }
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal(expected, block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_tabs
|
||||||
|
str = "hello\n dave"
|
||||||
|
assert_equal(str, basic_conv(str))
|
||||||
|
str = "hello\n\tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = "hello\n \tdave"
|
||||||
|
assert_equal("hello\n dave", basic_conv(str))
|
||||||
|
str = ".\t\t."
|
||||||
|
assert_equal(". .", basic_conv(str))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_whitespace
|
||||||
|
assert_equal("hello", basic_conv("hello"))
|
||||||
|
assert_equal("hello", basic_conv(" hello "))
|
||||||
|
assert_equal("hello", basic_conv(" \t \t hello\t\t"))
|
||||||
|
|
||||||
|
assert_equal("1\n 2\n 3", basic_conv("1\n 2\n 3"))
|
||||||
|
assert_equal("1\n 2\n 3", basic_conv(" 1\n 2\n 3"))
|
||||||
|
|
||||||
|
assert_equal("1\n 2\n 3\n1\n 2", basic_conv("1\n 2\n 3\n1\n 2"))
|
||||||
|
assert_equal("1\n 2\n 3\n1\n 2", basic_conv(" 1\n 2\n 3\n 1\n 2"))
|
||||||
|
|
||||||
|
assert_equal("1\n 2\n\n 3", basic_conv(" 1\n 2\n\n 3"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_types
|
||||||
|
str = "now is the time"
|
||||||
|
line_types(str, 'P')
|
||||||
|
|
||||||
|
str = "now is the time\nfor all good men"
|
||||||
|
line_types(str, 'PP')
|
||||||
|
|
||||||
|
str = "now is the time\n code\nfor all good men"
|
||||||
|
line_types(str, 'PVP')
|
||||||
|
|
||||||
|
str = "now is the time\n code\n more code\nfor all good men"
|
||||||
|
line_types(str, 'PVVP')
|
||||||
|
|
||||||
|
str = "now is\n---\nthe time"
|
||||||
|
line_types(str, 'PRP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
l1+
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLPLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l1.1
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLLLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l1.1
|
||||||
|
text
|
||||||
|
code
|
||||||
|
code
|
||||||
|
|
||||||
|
text
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLLPVVBPLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
1. l1
|
||||||
|
* l1.1
|
||||||
|
2. l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLLLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
[cat] l1
|
||||||
|
* l1.1
|
||||||
|
[dog] l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLLLP')
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
[cat] l1
|
||||||
|
continuation
|
||||||
|
[dog] l2
|
||||||
|
the time}
|
||||||
|
line_types(str, 'PLPLP')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_groups
|
||||||
|
str = "now is the time"
|
||||||
|
line_groups(str, ["L0: Paragraph\nnow is the time"] )
|
||||||
|
|
||||||
|
str = "now is the time\nfor all good men"
|
||||||
|
line_groups(str, ["L0: Paragraph\nnow is the time for all good men"] )
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is the time
|
||||||
|
code _line_ here
|
||||||
|
for all good men}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is the time",
|
||||||
|
"L0: Verbatim\n code _line_ here\n",
|
||||||
|
"L0: Paragraph\nfor all good men"
|
||||||
|
] )
|
||||||
|
|
||||||
|
str = "now is the time\n code\n more code\nfor all good men"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is the time",
|
||||||
|
"L0: Verbatim\n code\n more code\n",
|
||||||
|
"L0: Paragraph\nfor all good men"
|
||||||
|
] )
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
l1+
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1 l1+",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l1.1
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L2: ListStart\n",
|
||||||
|
"L2: ListItem\nl1.1",
|
||||||
|
"L2: ListEnd\n",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
* l1.1
|
||||||
|
text
|
||||||
|
code
|
||||||
|
code
|
||||||
|
|
||||||
|
text
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L2: ListStart\n",
|
||||||
|
"L2: ListItem\nl1.1 text",
|
||||||
|
"L2: Verbatim\n code\n code\n",
|
||||||
|
"L2: Paragraph\ntext",
|
||||||
|
"L2: ListEnd\n",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
1. l1
|
||||||
|
* l1.1
|
||||||
|
2. l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L2: ListStart\n",
|
||||||
|
"L2: ListItem\nl1.1",
|
||||||
|
"L2: ListEnd\n",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
[cat] l1
|
||||||
|
* l1.1
|
||||||
|
[dog] l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L2: ListStart\n",
|
||||||
|
"L2: ListItem\nl1.1",
|
||||||
|
"L2: ListEnd\n",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
[cat] l1
|
||||||
|
continuation
|
||||||
|
[dog] l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1 continuation",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_verbatim_merge
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
code1
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n code1\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
|
||||||
|
code1
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n\n code1\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
|
||||||
|
code1
|
||||||
|
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n\n code1\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
|
||||||
|
code1
|
||||||
|
|
||||||
|
code2
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n\n code1\n\n code2\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# Folds multiple blank lines
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
code
|
||||||
|
|
||||||
|
|
||||||
|
code1
|
||||||
|
|
||||||
|
the time}
|
||||||
|
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L0: Verbatim\n code\n\n code1\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_list_split
|
||||||
|
str = %{\
|
||||||
|
now is
|
||||||
|
* l1
|
||||||
|
1. n1
|
||||||
|
2. n2
|
||||||
|
* l2
|
||||||
|
the time}
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\nnow is",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl1",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nn1",
|
||||||
|
"L1: ListItem\nn2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L1: ListStart\n",
|
||||||
|
"L1: ListItem\nl2",
|
||||||
|
"L1: ListEnd\n",
|
||||||
|
"L0: Paragraph\nthe time"
|
||||||
|
])
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_headings
|
||||||
|
str = "= heading one"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Heading\nheading one"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = "=== heading three"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Heading\nheading three"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = "text\n === heading three"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\ntext",
|
||||||
|
"L0: Verbatim\n === heading three\n"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = "text\n code\n === heading three"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\ntext",
|
||||||
|
"L0: Verbatim\n code\n === heading three\n"
|
||||||
|
])
|
||||||
|
|
||||||
|
str = "text\n code\n=== heading three"
|
||||||
|
line_groups(str,
|
||||||
|
[ "L0: Paragraph\ntext",
|
||||||
|
"L0: Verbatim\n code\n",
|
||||||
|
"L0: Heading\nheading three"
|
||||||
|
])
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
526
lib/rdoc/options.rb
Normal file
526
lib/rdoc/options.rb
Normal file
|
@ -0,0 +1,526 @@
|
||||||
|
# We handle the parsing of options, and subsequently as a singleton
|
||||||
|
# object to be queried for option values
|
||||||
|
|
||||||
|
class Options
|
||||||
|
|
||||||
|
require 'singleton'
|
||||||
|
require 'getoptlong'
|
||||||
|
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
# files matching this pattern will be excluded
|
||||||
|
attr_accessor :exclude
|
||||||
|
|
||||||
|
# the name of the output directory
|
||||||
|
attr_accessor :op_dir
|
||||||
|
|
||||||
|
# the name to use for the output
|
||||||
|
attr_reader :op_name
|
||||||
|
|
||||||
|
# include private and protected methods in the
|
||||||
|
# output
|
||||||
|
attr_accessor :show_all
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Don't display progress as we process the files
|
||||||
|
attr_reader :quiet
|
||||||
|
|
||||||
|
# description of the output generator (set with the <tt>-fmt</tt>
|
||||||
|
# option
|
||||||
|
attr_accessor :generator
|
||||||
|
|
||||||
|
# and the list of files to be processed
|
||||||
|
attr_reader :files
|
||||||
|
|
||||||
|
# array of directories to search for files to satisfy an :include:
|
||||||
|
attr_reader :rdoc_include
|
||||||
|
|
||||||
|
# title to be used out the output
|
||||||
|
#attr_writer :title
|
||||||
|
|
||||||
|
# template to be used when generating output
|
||||||
|
attr_reader :template
|
||||||
|
|
||||||
|
# should diagrams be drawn
|
||||||
|
attr_reader :diagram
|
||||||
|
|
||||||
|
# should we draw fileboxes in diagrams
|
||||||
|
attr_reader :fileboxes
|
||||||
|
|
||||||
|
# include the '#' at the front of hyperlinked instance method names
|
||||||
|
attr_reader :show_hash
|
||||||
|
|
||||||
|
# image format for diagrams
|
||||||
|
attr_reader :image_format
|
||||||
|
|
||||||
|
# character-set
|
||||||
|
attr_reader :charset
|
||||||
|
|
||||||
|
# should source code be included inline, or displayed in a popup
|
||||||
|
attr_reader :inline_source
|
||||||
|
|
||||||
|
# should the output be placed into a single file
|
||||||
|
attr_reader :all_one_file
|
||||||
|
|
||||||
|
# the number of columns in a tab
|
||||||
|
attr_reader :tab_width
|
||||||
|
|
||||||
|
# include line numbers in the source listings
|
||||||
|
attr_reader :include_line_numbers
|
||||||
|
|
||||||
|
# pattern for additional attr_... style methods
|
||||||
|
attr_reader :extra_accessors
|
||||||
|
attr_reader :extra_accessor_flags
|
||||||
|
|
||||||
|
# URL of stylesheet
|
||||||
|
attr_reader :css
|
||||||
|
|
||||||
|
# URL of web cvs frontend
|
||||||
|
attr_reader :webcvs
|
||||||
|
|
||||||
|
# Are we promiscuous about showing module contents across
|
||||||
|
# multiple files
|
||||||
|
attr_reader :promiscuous
|
||||||
|
|
||||||
|
module OptionList
|
||||||
|
|
||||||
|
OPTION_LIST = [
|
||||||
|
[ "--accessor", "-A", "accessorname[,..]",
|
||||||
|
"comma separated list of additional class methods\n" +
|
||||||
|
"that should be treated like 'attr_reader' and\n" +
|
||||||
|
"friends. Option may be repeated. Each accessorname\n" +
|
||||||
|
"may have '=text' appended, in which case that text\n" +
|
||||||
|
"appears where the r/w/rw appears for normal accessors."],
|
||||||
|
|
||||||
|
[ "--all", "-a", nil,
|
||||||
|
"include all methods (not just public)\nin the output" ],
|
||||||
|
|
||||||
|
[ "--charset", "-c", "charset",
|
||||||
|
"specifies HTML character-set" ],
|
||||||
|
|
||||||
|
[ "--debug", "-D", nil,
|
||||||
|
"displays lots on internal stuff" ],
|
||||||
|
|
||||||
|
[ "--diagram", "-d", nil,
|
||||||
|
"Generate diagrams showing modules and classes.\n" +
|
||||||
|
"You need dot V1.8.6 or later to use the --diagram\n" +
|
||||||
|
"option correctly. Dot is available from\n"+
|
||||||
|
"http://www.research.att.com/sw/tools/graphviz/" ],
|
||||||
|
|
||||||
|
[ "--exclude", "-x", "pattern",
|
||||||
|
"do not process files or directories matching\n" +
|
||||||
|
"pattern. Files given explicitly on the command\n" +
|
||||||
|
"line will never be excluded." ],
|
||||||
|
|
||||||
|
[ "--extension", "-E", "new=old",
|
||||||
|
"Treat files ending with .new as if they ended with\n" +
|
||||||
|
".old. Using '-E cgi=rb' will cause xxx.cgi to be\n" +
|
||||||
|
"parsed as a Ruby file"],
|
||||||
|
|
||||||
|
[ "--fileboxes", "-F", nil,
|
||||||
|
"classes are put in boxes which represents\n" +
|
||||||
|
"files, where these classes reside. Classes\n" +
|
||||||
|
"shared between more than one file are\n" +
|
||||||
|
"shown with list of files that sharing them.\n" +
|
||||||
|
"Silently discarded if --diagram is not given\n" +
|
||||||
|
"Experimental." ],
|
||||||
|
|
||||||
|
[ "--fmt", "-f", "format name",
|
||||||
|
"set the output formatter (see below)" ],
|
||||||
|
|
||||||
|
[ "--help", "-h", nil,
|
||||||
|
"you're looking at it" ],
|
||||||
|
|
||||||
|
[ "--help-output", "-O", nil,
|
||||||
|
"explain the various output options" ],
|
||||||
|
|
||||||
|
[ "--image-format", "-I", "gif/png/jpg/jpeg",
|
||||||
|
"Sets output image format for diagrams. Can\n" +
|
||||||
|
"be png, gif, jpeg, jpg. If this option is\n" +
|
||||||
|
"omitted, png is used. Requires --diagram." ],
|
||||||
|
|
||||||
|
[ "--include", "-i", "dir[,dir...]",
|
||||||
|
"set (or add to) the list of directories\n" +
|
||||||
|
"to be searched when satisfying :include:\n" +
|
||||||
|
"requests. Can be used more than once." ],
|
||||||
|
|
||||||
|
[ "--inline-source", "-S", nil,
|
||||||
|
"Show method source code inline, rather\n" +
|
||||||
|
"than via a popup link" ],
|
||||||
|
|
||||||
|
[ "--line-numbers", "-N", nil,
|
||||||
|
"Include line numbers in the source code" ],
|
||||||
|
|
||||||
|
[ "--main", "-m", "name",
|
||||||
|
"'name' will be the initial page displayed" ],
|
||||||
|
|
||||||
|
[ "--one-file", "-1", nil,
|
||||||
|
"put all the output into a single file" ],
|
||||||
|
|
||||||
|
[ "--op", "-o", "dir",
|
||||||
|
"set the output directory" ],
|
||||||
|
|
||||||
|
[ "--opname", "-n", "name",
|
||||||
|
"Set the 'name' of the output. Has no\n" +
|
||||||
|
"effect for HTML." ],
|
||||||
|
|
||||||
|
[ "--promiscuous", "-p", nil,
|
||||||
|
"When documenting a file that contains a module\n" +
|
||||||
|
"or class also defined in other files, show\n" +
|
||||||
|
"all stuff for that module/class in each files\n" +
|
||||||
|
"page. By default, only show stuff defined in\n" +
|
||||||
|
"that particular file." ],
|
||||||
|
|
||||||
|
[ "--quiet", "-q", nil,
|
||||||
|
"don't show progress as we parse" ],
|
||||||
|
|
||||||
|
[ "--show-hash", "-H", nil,
|
||||||
|
"A name of the form #name in a comment\n" +
|
||||||
|
"is a possible hyperlink to an instance\n" +
|
||||||
|
"method name. When displayed, the '#' is\n" +
|
||||||
|
"removed unless this option is specified" ],
|
||||||
|
|
||||||
|
[ "--style", "-s", "stylesheet url",
|
||||||
|
"specifies the URL of a separate stylesheet." ],
|
||||||
|
|
||||||
|
[ "--tab-width", "-w", "n",
|
||||||
|
"Set the width of tab characters (default 8)"],
|
||||||
|
|
||||||
|
[ "--template", "-T", "template name",
|
||||||
|
"Set the template used when generating output" ],
|
||||||
|
|
||||||
|
[ "--title", "-t", "text",
|
||||||
|
"Set 'txt' as the title for the output" ],
|
||||||
|
|
||||||
|
[ "--version", "-v", nil,
|
||||||
|
"display RDoc's version" ],
|
||||||
|
|
||||||
|
[ "--webcvs", "-W", "url",
|
||||||
|
"Specify a URL for linking to a web frontend\n" +
|
||||||
|
"to CVS. If the URL contains a '\%s', the\n" +
|
||||||
|
"name of the current file will be substituted;\n" +
|
||||||
|
"if the URL doesn't contain a '\%s', the\n" +
|
||||||
|
"filename will be appended to it." ],
|
||||||
|
]
|
||||||
|
|
||||||
|
def OptionList.options
|
||||||
|
OPTION_LIST.map do |long, short, arg,|
|
||||||
|
[ long,
|
||||||
|
short,
|
||||||
|
arg ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def OptionList.strip_output(text)
|
||||||
|
text =~ /^\s+/
|
||||||
|
leading_spaces = $&
|
||||||
|
text.gsub!(/^#{leading_spaces}/, '')
|
||||||
|
$stdout.puts text
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Show an error and exit
|
||||||
|
|
||||||
|
def OptionList.error(msg)
|
||||||
|
$stderr.puts
|
||||||
|
$stderr.puts msg
|
||||||
|
$stderr.puts "\nFor help on options, try 'rdoc --help'\n\n"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Show usage and exit
|
||||||
|
|
||||||
|
def OptionList.usage(generator_names)
|
||||||
|
|
||||||
|
puts
|
||||||
|
puts(VERSION_STRING)
|
||||||
|
puts
|
||||||
|
|
||||||
|
name = File.basename($0)
|
||||||
|
OptionList.strip_output(<<-EOT)
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
#{name} [options] [names...]
|
||||||
|
|
||||||
|
Files are parsed, and the information they contain
|
||||||
|
collected, before any output is produced. This allows cross
|
||||||
|
references between all files to be resolved. If a name is a
|
||||||
|
directory, it is traversed. If no names are specified, all
|
||||||
|
Ruby files in the current directory (and subdirectories) are
|
||||||
|
processed.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
EOT
|
||||||
|
|
||||||
|
OPTION_LIST.each do |long, short, arg, desc|
|
||||||
|
opt = sprintf("%20s", "#{long}, #{short}")
|
||||||
|
oparg = sprintf("%-7s", arg)
|
||||||
|
print "#{opt} #{oparg}"
|
||||||
|
desc = desc.split("\n")
|
||||||
|
if arg.nil? || arg.length < 7
|
||||||
|
puts desc.shift
|
||||||
|
else
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
desc.each do |line|
|
||||||
|
puts(" "*28 + line)
|
||||||
|
end
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "\nAvailable output formatters: " +
|
||||||
|
generator_names.sort.join(', ') + "\n\n"
|
||||||
|
|
||||||
|
puts "For information on where the output goes, use\n\n"
|
||||||
|
puts " rdoc --help-output\n\n"
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def OptionList.help_output
|
||||||
|
OptionList.strip_output(<<-EOT)
|
||||||
|
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).
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- .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.
|
||||||
|
|
||||||
|
For information on other RDoc options, use "rdoc --help".
|
||||||
|
EOT
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse command line options. We're passed a hash containing
|
||||||
|
# output generators, keyed by the generator name
|
||||||
|
|
||||||
|
def parse(argv, generators)
|
||||||
|
old_argv = ARGV.dup
|
||||||
|
begin
|
||||||
|
ARGV.replace(argv)
|
||||||
|
@op_dir = "doc"
|
||||||
|
@op_name = nil
|
||||||
|
@show_all = false
|
||||||
|
@main_page = nil
|
||||||
|
@exclude = nil
|
||||||
|
@quiet = false
|
||||||
|
@generator_name = 'html'
|
||||||
|
@generator = generators[@generator_name]
|
||||||
|
@rdoc_include = []
|
||||||
|
@title = nil
|
||||||
|
@template = 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
|
||||||
|
|
||||||
|
@css = nil
|
||||||
|
@webcvs = nil
|
||||||
|
|
||||||
|
@charset = case $KCODE
|
||||||
|
when /^S/i
|
||||||
|
'Shift_JIS'
|
||||||
|
when /^E/i
|
||||||
|
'EUC-JP'
|
||||||
|
else
|
||||||
|
'iso-8859-1'
|
||||||
|
end
|
||||||
|
|
||||||
|
accessors = []
|
||||||
|
|
||||||
|
go = GetoptLong.new(*OptionList.options)
|
||||||
|
go.quiet = true
|
||||||
|
|
||||||
|
go.each do |opt, arg|
|
||||||
|
case opt
|
||||||
|
when "--all" then @show_all = true
|
||||||
|
when "--charset" then @charset = arg
|
||||||
|
when "--debug" then $DEBUG = true
|
||||||
|
when "--exclude" then @exclude = Regexp.new(arg)
|
||||||
|
when "--inline-source" then @inline_source = true
|
||||||
|
when "--line-numbers" then @include_line_numbers = true
|
||||||
|
when "--main" then @main_page = arg
|
||||||
|
when "--one-file" then @all_one_file = true
|
||||||
|
when "--op" then @op_dir = arg
|
||||||
|
when "--opname" then @op_name = arg
|
||||||
|
when "--promiscuous" then @promiscuous = true
|
||||||
|
when "--quiet" then @quiet = true
|
||||||
|
when "--show-hash" then @show_hash = true
|
||||||
|
when "--style" then @css = arg
|
||||||
|
when "--template" then @template = arg
|
||||||
|
when "--title" then @title = arg
|
||||||
|
when "--webcvs" then @webcvs = arg
|
||||||
|
|
||||||
|
when "--accessor"
|
||||||
|
arg.split(/,/).each do |accessor|
|
||||||
|
if accessor =~ /^(\w+)(=(.*))?$/
|
||||||
|
accessors << $1
|
||||||
|
@extra_accessor_flags[$1] = $3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
when "--diagram"
|
||||||
|
check_diagram
|
||||||
|
@diagram = true
|
||||||
|
|
||||||
|
when "--fileboxes"
|
||||||
|
@fileboxes = true if @diagram
|
||||||
|
|
||||||
|
when "--fmt"
|
||||||
|
@generator_name = arg.downcase
|
||||||
|
@generator = generators[@generator_name]
|
||||||
|
if !@generator
|
||||||
|
OptionList.error("Invalid output formatter")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @generator_name == "xml"
|
||||||
|
@all_one_file = true
|
||||||
|
@inline_source = true
|
||||||
|
end
|
||||||
|
|
||||||
|
when "--help"
|
||||||
|
OptionList.usage(generators.keys)
|
||||||
|
|
||||||
|
when "--help-output"
|
||||||
|
OptionList.help_output
|
||||||
|
|
||||||
|
when "--image-format"
|
||||||
|
if ['gif', 'png', 'jpeg', 'jpg'].include?(arg)
|
||||||
|
@image_format = arg
|
||||||
|
else
|
||||||
|
raise GetoptLong::InvalidOption.new("unknown image format: #{arg}")
|
||||||
|
end
|
||||||
|
|
||||||
|
when "--include"
|
||||||
|
@rdoc_include.concat arg.split(/\s*,\s*/)
|
||||||
|
|
||||||
|
when "--tab-width"
|
||||||
|
begin
|
||||||
|
@tab_width = Integer(arg)
|
||||||
|
rescue
|
||||||
|
$stderr.puts "Invalid tab width: '#{arg}'"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
when "--extension"
|
||||||
|
new, old = arg.split(/=/, 2)
|
||||||
|
OptionList.error("Invalid parameter to '-E'") unless new && old
|
||||||
|
unless RDoc::ParserFactory.alias_extension(old, new)
|
||||||
|
OptionList.error("Unknown extension .#{old} to -E")
|
||||||
|
end
|
||||||
|
|
||||||
|
when "--version"
|
||||||
|
puts VERSION_STRING
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@files = ARGV.dup
|
||||||
|
|
||||||
|
@rdoc_include << "." if @rdoc_include.empty?
|
||||||
|
|
||||||
|
check_files
|
||||||
|
|
||||||
|
# If no template was specified, use the default
|
||||||
|
# template for the output formatter
|
||||||
|
|
||||||
|
@template ||= @generator_name
|
||||||
|
|
||||||
|
# Generate a regexp from the accessors
|
||||||
|
unless accessors.empty?
|
||||||
|
re = '^(' + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$'
|
||||||
|
@extra_accessors = Regexp.new(re)
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error
|
||||||
|
OptionList.error(error.message)
|
||||||
|
|
||||||
|
ensure
|
||||||
|
ARGV.replace(old_argv)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def title
|
||||||
|
@title ||= "RDoc Documentation"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the title, but only if not already set. This means that a title set from
|
||||||
|
# the command line trumps one set in a source file
|
||||||
|
|
||||||
|
def title=(string)
|
||||||
|
@title ||= string
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Check that the right version of 'dot' is available.
|
||||||
|
# Unfortuately this doesn't work correctly under Windows NT,
|
||||||
|
# so we'll bypass the test under Windows
|
||||||
|
|
||||||
|
def check_diagram
|
||||||
|
return if RUBY_PLATFORM =~ /win/
|
||||||
|
|
||||||
|
ok = false
|
||||||
|
ver = nil
|
||||||
|
IO.popen("dot -V 2>&1") do |io|
|
||||||
|
ver = io.read
|
||||||
|
if ver =~ /dot\s+version(?:\s+gviz)?\s+(\d+)\.(\d+)/
|
||||||
|
ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
unless ok
|
||||||
|
if ver =~ /^dot version/
|
||||||
|
$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
|
||||||
|
# exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check that the files on the command line exist
|
||||||
|
|
||||||
|
def check_files
|
||||||
|
@files.each do |f|
|
||||||
|
stat = File.stat f rescue error("File not found: #{f}")
|
||||||
|
error("File '#{f}' not readable") unless stat.readable?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(str)
|
||||||
|
$stderr.puts str
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
287
lib/rdoc/parsers/parse_c.rb
Normal file
287
lib/rdoc/parsers/parse_c.rb
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
# We attempt to parse C extension files. Basically we look for
|
||||||
|
# the standard patterns that you find in extensions: <tt>rb_define_class,
|
||||||
|
# rb_define_method</tt> and so on. We also try to find the corresponding
|
||||||
|
# C source for the methods and extract comments, but if we fail
|
||||||
|
# we don't worry too much.
|
||||||
|
#
|
||||||
|
# The comments associated with a Ruby method are extracted from the C
|
||||||
|
# comment block associated with the routine that _implements_ that
|
||||||
|
# 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;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# in the method rb_ary_flatten. It will then use the comment from that
|
||||||
|
# method in the HTML output. This method must be in the same source file
|
||||||
|
# as the rb_define_method.
|
||||||
|
#
|
||||||
|
# C classes can be diagramed (see /tc/dl/ruby/ruby/error.c), and RDoc
|
||||||
|
# integrates C and Ruby source into one tree
|
||||||
|
|
||||||
|
# Classes and modules built in to the interpreter. We need
|
||||||
|
# these to define superclasses of user objects
|
||||||
|
|
||||||
|
require "rdoc/code_objects"
|
||||||
|
require "rdoc/parsers/parserfactory"
|
||||||
|
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
KNOWN_CLASSES = {
|
||||||
|
"rb_cObject" => "Object",
|
||||||
|
"rb_cArray" => "Array",
|
||||||
|
"rb_cBignum" => "Bignum",
|
||||||
|
"rb_cClass" => "Class",
|
||||||
|
"rb_cDir" => "Dir",
|
||||||
|
"rb_cData" => "Data",
|
||||||
|
"rb_cFalseClass" => "FalseClass",
|
||||||
|
"rb_cFile" => "File",
|
||||||
|
"rb_cFixnum" => "Fixnum",
|
||||||
|
"rb_cFloat" => "Float",
|
||||||
|
"rb_cHash" => "Hash",
|
||||||
|
"rb_cInteger" => "Integer",
|
||||||
|
"rb_cIO" => "IO",
|
||||||
|
"rb_cModule" => "Module",
|
||||||
|
"rb_cNilClass" => "NilClass",
|
||||||
|
"rb_cNumeric" => "Numeric",
|
||||||
|
"rb_cProc" => "Proc",
|
||||||
|
"rb_cRange" => "Range",
|
||||||
|
"rb_cRegexp" => "Regexp",
|
||||||
|
"rb_cString" => "String",
|
||||||
|
"rb_cSymbol" => "Symbol",
|
||||||
|
"rb_cThread" => "Thread",
|
||||||
|
"rb_cTime" => "Time",
|
||||||
|
"rb_cTrueClass" => "TrueClass",
|
||||||
|
"rb_cStruct" => "Struct",
|
||||||
|
"rb_eException" => "Exception",
|
||||||
|
"rb_eStandardError" => "StandardError",
|
||||||
|
"rb_eSystemExit" => "SystemExit",
|
||||||
|
"rb_eInterrupt" => "Interrupt",
|
||||||
|
"rb_eSignal" => "Signal",
|
||||||
|
"rb_eFatal" => "Fatal",
|
||||||
|
"rb_eArgError" => "ArgError",
|
||||||
|
"rb_eEOFError" => "EOFError",
|
||||||
|
"rb_eIndexError" => "IndexError",
|
||||||
|
"rb_eRangeError" => "RangeError",
|
||||||
|
"rb_eIOError" => "IOError",
|
||||||
|
"rb_eRuntimeError" => "RuntimeError",
|
||||||
|
"rb_eSecurityError" => "SecurityError",
|
||||||
|
"rb_eSystemCallError" => "SystemCallError",
|
||||||
|
"rb_eTypeError" => "TypeError",
|
||||||
|
"rb_eZeroDivError" => "ZeroDivError",
|
||||||
|
"rb_eNotImpError" => "NotImpError",
|
||||||
|
"rb_eNoMemError" => "NoMemError",
|
||||||
|
"rb_eFloatDomainError" => "FloatDomainError",
|
||||||
|
"rb_eScriptError" => "ScriptError",
|
||||||
|
"rb_eNameError" => "NameError",
|
||||||
|
"rb_eSyntaxError" => "SyntaxError",
|
||||||
|
"rb_eLoadError" => "LoadError",
|
||||||
|
|
||||||
|
"rb_mKernel" => "Kernel",
|
||||||
|
"rb_mComparable" => "Comparable",
|
||||||
|
"rb_mEnumerable" => "Enumerable",
|
||||||
|
"rb_mPrecision" => "Precision",
|
||||||
|
"rb_mErrno" => "Errno",
|
||||||
|
"rb_mFileTest" => "FileTest",
|
||||||
|
"rb_mGC" => "GC",
|
||||||
|
"rb_mMath" => "Math",
|
||||||
|
"rb_mProcess" => "Process"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# See rdoc/c_parse.rb
|
||||||
|
|
||||||
|
class C_Parser
|
||||||
|
|
||||||
|
|
||||||
|
extend ParserFactory
|
||||||
|
parse_files_matching(/\.(c|cc|cpp|CC)$/)
|
||||||
|
|
||||||
|
|
||||||
|
# prepare to parse a C file
|
||||||
|
def initialize(top_level, file_name, body, options)
|
||||||
|
@known_classes = KNOWN_CLASSES.dup
|
||||||
|
@body = body
|
||||||
|
@options = options
|
||||||
|
@top_level = top_level
|
||||||
|
@classes = Hash.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract the classes/modules and methods from a C file
|
||||||
|
# and return the corresponding top-level object
|
||||||
|
def scan
|
||||||
|
remove_commented_out_lines
|
||||||
|
do_classes
|
||||||
|
do_methods
|
||||||
|
@top_level
|
||||||
|
end
|
||||||
|
|
||||||
|
#######
|
||||||
|
private
|
||||||
|
#######
|
||||||
|
|
||||||
|
# remove lines that are commented out that might otherwise get
|
||||||
|
# picked up when scanning for classes and methods
|
||||||
|
|
||||||
|
def remove_commented_out_lines
|
||||||
|
@body.gsub!(%r{//.*rb_define_}, '//')
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_class_module(var_name, class_mod, class_name, parent, in_module)
|
||||||
|
@known_classes[var_name] = class_name
|
||||||
|
parent_name = @known_classes[parent] || parent
|
||||||
|
|
||||||
|
if in_module
|
||||||
|
enclosure = @classes[in_module]
|
||||||
|
unless enclosure
|
||||||
|
$stderr.puts("Enclosing class/module '#{in_module}' for " +
|
||||||
|
class_mod + " #{class_name} not known")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
enclosure = @top_level
|
||||||
|
end
|
||||||
|
|
||||||
|
if class_mod == "class"
|
||||||
|
cm = enclosure.add_class(NormalClass, class_name, parent_name)
|
||||||
|
else
|
||||||
|
cm = enclosure.add_module(NormalModule, class_name)
|
||||||
|
end
|
||||||
|
cm.record_location(enclosure.toplevel)
|
||||||
|
@classes[var_name] = cm
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_classes
|
||||||
|
@body.scan(/(\w+)\s* = \s*rb_define_module\(\s*"(\w+)"\s*\)/mx) do
|
||||||
|
|var_name, class_name|
|
||||||
|
handle_class_module(var_name, "module", class_name, nil, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
@body.scan(/(\w+)\s* = \s*rb_define_module_under
|
||||||
|
\(
|
||||||
|
\s*(\w+),
|
||||||
|
\s*"(\w+)"
|
||||||
|
\)/mx) do
|
||||||
|
|
||||||
|
|var_name, in_module, class_name|
|
||||||
|
handle_class_module(var_name, "module", class_name, nil, in_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
@body.scan(/(\w+)\s* = \s*rb_define_class
|
||||||
|
\(
|
||||||
|
\s*"(\w+)",
|
||||||
|
\s*(\w+)\s*
|
||||||
|
\)/mx) do
|
||||||
|
|
||||||
|
|var_name, class_name, parent|
|
||||||
|
handle_class_module(var_name, "class", class_name, parent, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
@body.scan(/(\w+)\s* = \s*rb_define_class_under
|
||||||
|
\(
|
||||||
|
\s*(\w+),
|
||||||
|
\s*"(\w+)",
|
||||||
|
\s*(\w+)\s*
|
||||||
|
\)/mx) do
|
||||||
|
|
||||||
|
|var_name, in_module, class_name, parent|
|
||||||
|
handle_class_module(var_name, "class", class_name, parent, in_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def do_methods
|
||||||
|
@body.scan(/rb_define_(singleton_method|method|module_function)\(\s*(\w+),
|
||||||
|
\s*"([^"]+)",
|
||||||
|
\s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
|
||||||
|
\s*(-?\w+)\s*\)/xm) do
|
||||||
|
|type, var_name, meth_name, meth_body, param_count|
|
||||||
|
|
||||||
|
class_name = @known_classes[var_name] || var_name
|
||||||
|
class_obj = @classes[var_name]
|
||||||
|
if class_obj
|
||||||
|
meth_obj = AnyMethod.new("", meth_name)
|
||||||
|
meth_obj.singleton = type == "singleton_method"
|
||||||
|
|
||||||
|
p_count = (Integer(param_count) rescue -1)
|
||||||
|
|
||||||
|
if p_count < 0
|
||||||
|
meth_obj.params = "(...)"
|
||||||
|
elsif p_count == 0
|
||||||
|
meth_obj.params = "()"
|
||||||
|
else
|
||||||
|
meth_obj.params = "(" +
|
||||||
|
(1..p_count).map{|i| "p#{i}"}.join(", ") +
|
||||||
|
")"
|
||||||
|
end
|
||||||
|
|
||||||
|
find_body(meth_body, meth_obj)
|
||||||
|
class_obj.add_method(meth_obj)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find the C code corresponding to a c method
|
||||||
|
def find_body(meth_name, meth_obj)
|
||||||
|
if @body =~ %r{((?>/\*.*?\*/\s+))(static\s+)?VALUE\s+#{meth_name}
|
||||||
|
\s*(\(.*?\)).*?^}xm
|
||||||
|
comment, params = $1, $3
|
||||||
|
body_text = $&
|
||||||
|
|
||||||
|
# see if we can find the whole body
|
||||||
|
|
||||||
|
re = Regexp.escape(body_text) + "[^(]*^{.*?^}"
|
||||||
|
if Regexp.new(re, Regexp::MULTILINE).match(@body)
|
||||||
|
body_text = $&
|
||||||
|
end
|
||||||
|
|
||||||
|
meth_obj.params = params
|
||||||
|
meth_obj.start_collecting_tokens
|
||||||
|
meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text))
|
||||||
|
meth_obj.comment = mangle_comment(comment)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
118
lib/rdoc/parsers/parse_f95.rb
Normal file
118
lib/rdoc/parsers/parse_f95.rb
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
# Parse a Fortran 95 file.
|
||||||
|
|
||||||
|
require "rdoc/code_objects"
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
# See rdoc/parsers/parse_f95.rb
|
||||||
|
|
||||||
|
class Token
|
||||||
|
|
||||||
|
NO_TEXT = "??".freeze
|
||||||
|
|
||||||
|
def initialize(line_no, char_no)
|
||||||
|
@line_no = line_no
|
||||||
|
@char_no = char_no
|
||||||
|
@text = NO_TEXT
|
||||||
|
end
|
||||||
|
# Because we're used in contexts that expect to return a token,
|
||||||
|
# we set the text string and then return ourselves
|
||||||
|
def set_text(text)
|
||||||
|
@text = text
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :line_no, :char_no, :text
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Fortran95parser
|
||||||
|
|
||||||
|
extend ParserFactory
|
||||||
|
parse_files_matching(/\.(f9(0|5)|F)$/)
|
||||||
|
|
||||||
|
# prepare to parse a Fortran 95 file
|
||||||
|
def initialize(top_level, file_name, body, options)
|
||||||
|
@body = body
|
||||||
|
@options = options
|
||||||
|
@top_level = top_level
|
||||||
|
@progress = $stderr unless options.quiet
|
||||||
|
end
|
||||||
|
|
||||||
|
# devine code constructs
|
||||||
|
def scan
|
||||||
|
|
||||||
|
# modules and programs
|
||||||
|
if @body =~ /^(module|program)\s+(\w+)/i
|
||||||
|
progress "m"
|
||||||
|
f9x_module = @top_level.add_module NormalClass, $2
|
||||||
|
f9x_module.record_location @top_level
|
||||||
|
first_comment, second_comment = $`.gsub(/^!\s?/,"").split "\n\s*\n"
|
||||||
|
if second_comment
|
||||||
|
@top_level.comment = first_comment if first_comment
|
||||||
|
f9x_module.comment = second_comment
|
||||||
|
else
|
||||||
|
f9x_module.comment = first_comment if first_comment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# use modules
|
||||||
|
remaining_code = @body
|
||||||
|
while remaining_code =~ /^\s*use\s+(\w+)/i
|
||||||
|
remaining_code = $~.post_match
|
||||||
|
progress "."
|
||||||
|
f9x_module.add_include Include.new($1, "") if f9x_module
|
||||||
|
end
|
||||||
|
|
||||||
|
# subroutines
|
||||||
|
remaining_code = @body
|
||||||
|
while remaining_code =~ /^\s*subroutine\s+(\w+)\s*\((.*?)\)/im
|
||||||
|
remaining_code = $~.post_match
|
||||||
|
subroutine = AnyMethod.new("Text", $1)
|
||||||
|
subroutine.singleton = false
|
||||||
|
|
||||||
|
prematchText = $~.pre_match
|
||||||
|
params = $2
|
||||||
|
params.gsub!(/&/,'')
|
||||||
|
subroutine.params = params
|
||||||
|
comment = find_comments prematchText
|
||||||
|
subroutine.comment = comment if comment
|
||||||
|
|
||||||
|
subroutine.start_collecting_tokens
|
||||||
|
remaining_code =~ /^\s*end\s+subroutine/i
|
||||||
|
code = "subroutine #{subroutine.name} (#{subroutine.params})\n"
|
||||||
|
code += $~.pre_match
|
||||||
|
code += "\nend subroutine\n"
|
||||||
|
subroutine.add_token Token.new(1,1).set_text(code)
|
||||||
|
|
||||||
|
progress "s"
|
||||||
|
f9x_module.add_method subroutine if f9x_module
|
||||||
|
end
|
||||||
|
|
||||||
|
@top_level
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_comments text
|
||||||
|
lines = text.split("\n").reverse
|
||||||
|
comment_block = Array.new
|
||||||
|
lines.each do |line|
|
||||||
|
break if line =~ /^\s*\w/
|
||||||
|
comment_block.unshift line.sub(/^!\s?/,"")
|
||||||
|
end
|
||||||
|
nice_lines = comment_block.join("\n").split "\n\s*\n"
|
||||||
|
nice_lines.shift
|
||||||
|
nice_lines.shift
|
||||||
|
nice_lines.shift
|
||||||
|
end
|
||||||
|
|
||||||
|
def progress(char)
|
||||||
|
unless @options.quiet
|
||||||
|
@progress.print(char)
|
||||||
|
@progress.flush
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end # class Fortran95parser
|
||||||
|
|
||||||
|
end # module RDoc
|
2494
lib/rdoc/parsers/parse_rb.rb
Normal file
2494
lib/rdoc/parsers/parse_rb.rb
Normal file
File diff suppressed because it is too large
Load diff
37
lib/rdoc/parsers/parse_simple.rb
Normal file
37
lib/rdoc/parsers/parse_simple.rb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Parse a non-source file. We basically take the whole thing
|
||||||
|
# as one big comment. If the first character in the file
|
||||||
|
# is '#', we strip leading pound signs.
|
||||||
|
|
||||||
|
|
||||||
|
require "rdoc/code_objects"
|
||||||
|
require "rdoc/markup/simple_markup/preprocess"
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
# See rdoc/parsers/parse_c.rb
|
||||||
|
|
||||||
|
class SimpleParser
|
||||||
|
|
||||||
|
# prepare to parse a plain file
|
||||||
|
def initialize(top_level, file_name, body, options)
|
||||||
|
|
||||||
|
preprocess = SM::PreProcess.new(file_name, options.rdoc_include)
|
||||||
|
|
||||||
|
preprocess.handle(body) do |directive, param|
|
||||||
|
$stderr.puts "Unrecognized directive '#{directive}' in #{file_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@body = body
|
||||||
|
@options = options
|
||||||
|
@top_level = top_level
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract the file contents and attach them to the toplevel as a
|
||||||
|
# comment
|
||||||
|
|
||||||
|
def scan
|
||||||
|
# @body.gsub(/^(\s\n)+/, '')
|
||||||
|
@top_level.comment = @body
|
||||||
|
@top_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
86
lib/rdoc/parsers/parserfactory.rb
Normal file
86
lib/rdoc/parsers/parserfactory.rb
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
require "rdoc/parsers/parse_simple"
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
# A parser is simple a class that implements
|
||||||
|
#
|
||||||
|
# #initialize(file_name, body, options)
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
#
|
||||||
|
# #scan
|
||||||
|
#
|
||||||
|
# The initialize method takes a file name to be used, the body of the
|
||||||
|
# file, and an RDoc::Options object. The scan method is then called
|
||||||
|
# to return an appropriately parsed TopLevel code object.
|
||||||
|
|
||||||
|
# The ParseFactory is used to redirect to the correct parser given a filename
|
||||||
|
# extension. This magic works because individual parsers have to register
|
||||||
|
# themselves with us as they are loaded in. The do this using the following
|
||||||
|
# incantation
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# require "rdoc/parsers/parsefactory"
|
||||||
|
#
|
||||||
|
# module RDoc
|
||||||
|
#
|
||||||
|
# class XyzParser
|
||||||
|
# extend ParseFactory <<<<
|
||||||
|
# parse_files_matching /\.xyz$/ <<<<
|
||||||
|
#
|
||||||
|
# def initialize(file_name, body, options)
|
||||||
|
# ...
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def scan
|
||||||
|
# ...
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module ParserFactory
|
||||||
|
|
||||||
|
@@parsers = []
|
||||||
|
|
||||||
|
Parsers = Struct.new(:regexp, :parser)
|
||||||
|
|
||||||
|
# Record the fact that a particular class parses files that
|
||||||
|
# match a given extension
|
||||||
|
|
||||||
|
def parse_files_matching(regexp)
|
||||||
|
@@parsers.unshift Parsers.new(regexp, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a parser that can handle a particular extension
|
||||||
|
|
||||||
|
def ParserFactory.can_parse(file_name)
|
||||||
|
@@parsers.find {|p| p.regexp.match(file_name) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Alias an extension to another extension. After this call,
|
||||||
|
# files ending "new_ext" will be parsed using the same parser
|
||||||
|
# as "old_ext"
|
||||||
|
|
||||||
|
def ParserFactory.alias_extension(old_ext, new_ext)
|
||||||
|
parser = ParserFactory.can_parse("xxx.#{old_ext}")
|
||||||
|
return false unless parser
|
||||||
|
@@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find the correct parser for a particular file name. Return a
|
||||||
|
# SimpleParser for ones that we don't know
|
||||||
|
|
||||||
|
def ParserFactory.parser_for(top_level, file_name, body, options)
|
||||||
|
parser_description = can_parse(file_name)
|
||||||
|
if parser_description
|
||||||
|
parser = parser_description.parser
|
||||||
|
else
|
||||||
|
parser = SimpleParser
|
||||||
|
end
|
||||||
|
parser.new(top_level, file_name, body, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
219
lib/rdoc/rdoc.rb
Normal file
219
lib/rdoc/rdoc.rb
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
# See README.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
RDOC_VERSION = "1.0pr1"
|
||||||
|
|
||||||
|
rcs = '$Date$ $Revision$'.
|
||||||
|
gsub(/\$/, '').
|
||||||
|
sub(/Date: /, ': ').
|
||||||
|
sub(/ Revision: (\S+)/) { "(#$1)" }
|
||||||
|
|
||||||
|
VERSION_STRING = %{RDoc V} + RDOC_VERSION + rcs
|
||||||
|
|
||||||
|
|
||||||
|
require 'rdoc/parsers/parse_rb.rb'
|
||||||
|
require 'rdoc/parsers/parse_c.rb'
|
||||||
|
require 'rdoc/parsers/parse_f95.rb'
|
||||||
|
|
||||||
|
require 'rdoc/parsers/parse_simple.rb'
|
||||||
|
require 'rdoc/options'
|
||||||
|
|
||||||
|
require 'rdoc/diagram'
|
||||||
|
|
||||||
|
require 'find'
|
||||||
|
require 'ftools'
|
||||||
|
|
||||||
|
# We put rdoc stuff in the RDoc module to avoid namespace
|
||||||
|
# clutter.
|
||||||
|
#
|
||||||
|
# ToDo: This isn't universally true.
|
||||||
|
|
||||||
|
module RDoc
|
||||||
|
|
||||||
|
# Exception thrown by any rdoc error. Only the #message part is
|
||||||
|
# of use externally.
|
||||||
|
|
||||||
|
class RDocError < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encapsulate the production of rdoc documentation. Basically
|
||||||
|
# you can use this as you would invoke rdoc from the command
|
||||||
|
# line:
|
||||||
|
#
|
||||||
|
# rdoc = RDoc::RDoc.new
|
||||||
|
# rdoc.document(args)
|
||||||
|
#
|
||||||
|
# where _args_ is an array of strings, each corresponding to
|
||||||
|
# an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
|
||||||
|
# for details.
|
||||||
|
|
||||||
|
class RDoc
|
||||||
|
|
||||||
|
##
|
||||||
|
# This is the list of output generators that we
|
||||||
|
# support
|
||||||
|
|
||||||
|
Generator = Struct.new(:file_name, :class_name, :key)
|
||||||
|
|
||||||
|
GENERATORS = {}
|
||||||
|
$:.collect {|d|
|
||||||
|
File::expand_path(d)
|
||||||
|
}.find_all {|d|
|
||||||
|
File::directory?("#{d}/rdoc/generators")
|
||||||
|
}.each {|dir|
|
||||||
|
Dir::entries("#{dir}/rdoc/generators").each {|gen|
|
||||||
|
next unless /(\w+)_generator.rb$/ =~ gen
|
||||||
|
type = $1
|
||||||
|
unless GENERATORS.has_key? type
|
||||||
|
GENERATORS[type] = Generator.new("rdoc/generators/#{gen}",
|
||||||
|
"#{type.upcase}Generator".intern,
|
||||||
|
type)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#######
|
||||||
|
private
|
||||||
|
#######
|
||||||
|
|
||||||
|
##
|
||||||
|
# Report an error message and exit
|
||||||
|
|
||||||
|
def error(msg)
|
||||||
|
raise RDocError.new(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Create an output dir if it doesn't exist. If it does
|
||||||
|
# exist, but doesn't contain the flag file <tt>created.rid</tt>
|
||||||
|
# then we refuse to use it, as we may clobber some
|
||||||
|
# manually generated documentation
|
||||||
|
|
||||||
|
def setup_output_dir(op_dir)
|
||||||
|
flag_file = File.join(op_dir, "created.rid")
|
||||||
|
if File.exist?(op_dir)
|
||||||
|
unless File.directory?(op_dir)
|
||||||
|
error "'#{op_dir}' exists, and is not a directory"
|
||||||
|
end
|
||||||
|
unless File.file?(flag_file)
|
||||||
|
error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
|
||||||
|
"isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
|
||||||
|
"destroying any of your existing files, you'll need to\n" +
|
||||||
|
"specify a different output directory name (using the\n" +
|
||||||
|
"--op <dir> option).\n\n"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
File.makedirs(op_dir)
|
||||||
|
end
|
||||||
|
File.open(flag_file, "w") {|f| f.puts Time.now }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Given a list of files and directories, create a list
|
||||||
|
# of all the Ruby files they contain.
|
||||||
|
|
||||||
|
def normalized_file_list(options, *relative_files)
|
||||||
|
file_list = []
|
||||||
|
|
||||||
|
relative_files.each do |rel_file_name|
|
||||||
|
|
||||||
|
case type = File.stat(rel_file_name).ftype
|
||||||
|
when "file"
|
||||||
|
file_list << rel_file_name
|
||||||
|
when "directory"
|
||||||
|
next if options.exclude && options.exclude =~ rel_file_name
|
||||||
|
Find.find(rel_file_name) do |fn|
|
||||||
|
next if options.exclude && options.exclude =~ fn
|
||||||
|
next unless ParserFactory.can_parse(fn)
|
||||||
|
next unless File.file?(fn)
|
||||||
|
|
||||||
|
file_list << fn.sub(%r{\./}, '')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
file_list
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse each file on the command line, recursively entering
|
||||||
|
# directories
|
||||||
|
|
||||||
|
def parse_files(options)
|
||||||
|
|
||||||
|
file_info = []
|
||||||
|
|
||||||
|
files = options.files
|
||||||
|
files = ["."] if files.empty?
|
||||||
|
|
||||||
|
file_list = normalized_file_list(options, *files)
|
||||||
|
|
||||||
|
file_list.each do |fn|
|
||||||
|
$stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
|
||||||
|
|
||||||
|
content = File.open(fn, "r") {|f| f.read}
|
||||||
|
|
||||||
|
top_level = TopLevel.new(fn)
|
||||||
|
parser = ParserFactory.parser_for(top_level, fn, content, options)
|
||||||
|
file_info << parser.scan
|
||||||
|
end
|
||||||
|
|
||||||
|
file_info
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
#
|
||||||
|
# Format up one or more files according to the given arguments.
|
||||||
|
# For simplicity, _argv_ is an array of strings, equivalent to the
|
||||||
|
# strings that would be passed on the command line. (This isn't a
|
||||||
|
# coincidence, as we _do_ pass in ARGV when running
|
||||||
|
# interactively). For a list of options, see rdoc/rdoc.rb. By
|
||||||
|
# default, output will be stored in a directory called +doc+ below
|
||||||
|
# the current directory, so make sure you're somewhere writable
|
||||||
|
# before invoking.
|
||||||
|
#
|
||||||
|
# Throws: RDocError on error
|
||||||
|
|
||||||
|
def document(argv)
|
||||||
|
|
||||||
|
TopLevel::reset
|
||||||
|
|
||||||
|
options = Options.instance
|
||||||
|
options.parse(argv, GENERATORS)
|
||||||
|
|
||||||
|
file_info = parse_files(options)
|
||||||
|
|
||||||
|
gen = options.generator
|
||||||
|
|
||||||
|
$stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet
|
||||||
|
|
||||||
|
require gen.file_name
|
||||||
|
|
||||||
|
gen_class = Generators.const_get(gen.class_name)
|
||||||
|
|
||||||
|
unless file_info.empty?
|
||||||
|
gen = gen_class.for(options)
|
||||||
|
|
||||||
|
pwd = Dir.pwd
|
||||||
|
|
||||||
|
unless options.all_one_file
|
||||||
|
setup_output_dir(options.op_dir)
|
||||||
|
Dir.chdir(options.op_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
Diagram.new(file_info, options).draw if options.diagram
|
||||||
|
gen.generate(file_info)
|
||||||
|
ensure
|
||||||
|
Dir.chdir(pwd)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
234
lib/rdoc/template.rb
Normal file
234
lib/rdoc/template.rb
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
# Cheap-n-cheerful HTML page template system. You create a
|
||||||
|
# template containing:
|
||||||
|
#
|
||||||
|
# * variable names between percent signs (<tt>%fred%</tt>)
|
||||||
|
# * blocks of repeating stuff:
|
||||||
|
#
|
||||||
|
# START:key
|
||||||
|
# ... stuff
|
||||||
|
# END:key
|
||||||
|
#
|
||||||
|
# You feed the code a hash. For simple variables, the values
|
||||||
|
# are resolved directly from the hash. For blocks, the hash entry
|
||||||
|
# corresponding to +key+ will be an array of hashes. The block will
|
||||||
|
# be generated once for each entry. Blocks can be nested arbitrarily
|
||||||
|
# deeply.
|
||||||
|
#
|
||||||
|
# The template may also contain
|
||||||
|
#
|
||||||
|
# IF:key
|
||||||
|
# ... stuff
|
||||||
|
# ENDIF:key
|
||||||
|
#
|
||||||
|
# _stuff_ will only be included in the output if the corresponding
|
||||||
|
# key is set in the value hash.
|
||||||
|
#
|
||||||
|
# Usage: Given a set of templates <tt>T1, T2,</tt> etc
|
||||||
|
#
|
||||||
|
# values = { "name" => "Dave", state => "TX" }
|
||||||
|
#
|
||||||
|
# t = TemplatePage.new(T1, T2, T3)
|
||||||
|
# File.open(name, "w") {|f| t.write_html_on(f, values)}
|
||||||
|
# or
|
||||||
|
# res = ''
|
||||||
|
# t.write_html_on(res, values)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
class TemplatePage
|
||||||
|
|
||||||
|
##########
|
||||||
|
# A context holds a stack of key/value pairs (like a symbol
|
||||||
|
# table). When asked to resolve a key, it first searches the top of
|
||||||
|
# the stack, then the next level, and so on until it finds a match
|
||||||
|
# (or runs out of entries)
|
||||||
|
|
||||||
|
class Context
|
||||||
|
def initialize
|
||||||
|
@stack = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def push(hash)
|
||||||
|
@stack.push(hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop
|
||||||
|
@stack.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a scalar value, throwing an exception if not found. This
|
||||||
|
# method is used when substituting the %xxx% constructs
|
||||||
|
|
||||||
|
def find_scalar(key)
|
||||||
|
@stack.reverse_each do |level|
|
||||||
|
if val = level[key]
|
||||||
|
return val unless val.kind_of? Array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise "Template error: can't find variable '#{key}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Lookup any key in the stack of hashes
|
||||||
|
|
||||||
|
def lookup(key)
|
||||||
|
@stack.reverse_each do |level|
|
||||||
|
val = level[key]
|
||||||
|
return val if val
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Simple class to read lines out of a string
|
||||||
|
|
||||||
|
class LineReader
|
||||||
|
# we're initialized with an array of lines
|
||||||
|
def initialize(lines)
|
||||||
|
@lines = lines
|
||||||
|
end
|
||||||
|
|
||||||
|
# read the next line
|
||||||
|
def read
|
||||||
|
@lines.shift
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a list of lines up to the line that matches
|
||||||
|
# a pattern. That last line is discarded.
|
||||||
|
def read_up_to(pattern)
|
||||||
|
res = []
|
||||||
|
while line = read
|
||||||
|
if pattern.match(line)
|
||||||
|
return LineReader.new(res)
|
||||||
|
else
|
||||||
|
res << line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise "Missing end tag in template: #{pattern.source}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of ourselves that can be modified without
|
||||||
|
# affecting us
|
||||||
|
def dup
|
||||||
|
LineReader.new(@lines.dup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# +templates+ is an array of strings containing the templates.
|
||||||
|
# We start at the first, and substitute in subsequent ones
|
||||||
|
# where the string <tt>!INCLUDE!</tt> occurs. For example,
|
||||||
|
# we could have the overall page template containing
|
||||||
|
#
|
||||||
|
# <html><body>
|
||||||
|
# <h1>Master</h1>
|
||||||
|
# !INCLUDE!
|
||||||
|
# </bost></html>
|
||||||
|
#
|
||||||
|
# and substitute subpages in to it by passing [master, sub_page].
|
||||||
|
# This gives us a cheap way of framing pages
|
||||||
|
|
||||||
|
def initialize(*templates)
|
||||||
|
result = "!INCLUDE!"
|
||||||
|
templates.each do |content|
|
||||||
|
result.sub!(/!INCLUDE!/, content)
|
||||||
|
end
|
||||||
|
@lines = LineReader.new(result.split($/))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Render the templates into HTML, storing the result on +op+
|
||||||
|
# using the method <tt><<</tt>. The <tt>value_hash</tt> contains
|
||||||
|
# key/value pairs used to drive the substitution (as described above)
|
||||||
|
|
||||||
|
def write_html_on(op, value_hash)
|
||||||
|
@context = Context.new
|
||||||
|
op << substitute_into(@lines, value_hash).tr("\000", '\\')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Substitute a set of key/value pairs into the given template.
|
||||||
|
# Keys with scalar values have them substituted directly into
|
||||||
|
# the page. Those with array values invoke <tt>substitute_array</tt>
|
||||||
|
# (below), which examples a block of the template once for each
|
||||||
|
# row in the array.
|
||||||
|
#
|
||||||
|
# This routine also copes with the <tt>IF:</tt>_key_ directive,
|
||||||
|
# removing chunks of the template if the corresponding key
|
||||||
|
# does not appear in the hash, and the START: directive, which
|
||||||
|
# loops its contents for each value in an array
|
||||||
|
|
||||||
|
def substitute_into(lines, values)
|
||||||
|
@context.push(values)
|
||||||
|
skip_to = nil
|
||||||
|
result = []
|
||||||
|
|
||||||
|
while line = lines.read
|
||||||
|
|
||||||
|
case line
|
||||||
|
|
||||||
|
when /^IF:(\w+)/
|
||||||
|
lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
|
||||||
|
|
||||||
|
when /^IFNOT:(\w+)/
|
||||||
|
lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
|
||||||
|
|
||||||
|
when /^ENDIF:/
|
||||||
|
;
|
||||||
|
|
||||||
|
when /^START:(\w+)/
|
||||||
|
tag = $1
|
||||||
|
body = lines.read_up_to(/^END:#{tag}/)
|
||||||
|
inner_values = @context.lookup(tag)
|
||||||
|
raise "unknown tag: #{tag}" unless inner_values
|
||||||
|
raise "not array: #{tag}" unless inner_values.kind_of?(Array)
|
||||||
|
inner_values.each do |vals|
|
||||||
|
result << substitute_into(body.dup, vals)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result << expand_line(line.dup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@context.pop
|
||||||
|
|
||||||
|
result.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an individual line, we look for %xxx% constructs and
|
||||||
|
# HREF:ref:name: constructs, substituting for each.
|
||||||
|
|
||||||
|
def expand_line(line)
|
||||||
|
# Generate a cross reference if a reference is given,
|
||||||
|
# otherwise just fill in the name part
|
||||||
|
|
||||||
|
line.gsub!(/HREF:(\w+?):(\w+?):/) {
|
||||||
|
ref = @context.lookup($1)
|
||||||
|
name = @context.find_scalar($2)
|
||||||
|
|
||||||
|
if ref and !ref.kind_of?(Array)
|
||||||
|
"<a href=\"#{ref}\">#{name}</a>"
|
||||||
|
else
|
||||||
|
name
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
# Substitute in values for %xxx% constructs. This is made complex
|
||||||
|
# because the replacement string may contain characters that are
|
||||||
|
# meaningful to the regexp (like \1)
|
||||||
|
|
||||||
|
line = line.gsub(/%([a-zA-Z]\w*)%/) {
|
||||||
|
val = @context.find_scalar($1)
|
||||||
|
val.tr('\\', "\000")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
line
|
||||||
|
rescue Exception => e
|
||||||
|
$stderr.puts "Error in template: #{e}"
|
||||||
|
$stderr.puts "Original line: #{line}"
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
25
lib/rdoc/tokenstream.rb
Normal file
25
lib/rdoc/tokenstream.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# A TokenStream is a list of tokens, gathered during the parse
|
||||||
|
# of some entity (say a method). Entities populate these streams
|
||||||
|
# by being registered with the lexer. Any class can collect tokens
|
||||||
|
# by including TokenStream. From the outside, you use such an object
|
||||||
|
# by calling the start_collecting_tokens method, followed by calls
|
||||||
|
# to add_token and pop_token
|
||||||
|
|
||||||
|
module TokenStream
|
||||||
|
def token_stream
|
||||||
|
@token_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_collecting_tokens
|
||||||
|
@token_stream = []
|
||||||
|
end
|
||||||
|
def add_token(tk)
|
||||||
|
@token_stream << tk
|
||||||
|
end
|
||||||
|
def add_tokens(tks)
|
||||||
|
tks.each {|tk| add_token(tk)}
|
||||||
|
end
|
||||||
|
def pop_token
|
||||||
|
@token_stream.pop
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue