зеркало из https://github.com/github/ruby.git
Renamespace lib/rdoc/markup from SM::SimpleMarkup to RDoc::Markup.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15033 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
cbd4604c53
Коммит
fcb0b1f503
|
@ -1,3 +1,8 @@
|
|||
Mon Jan 14 12:33:07 2008 Eric Hodel <drbrain@segment7.net>
|
||||
|
||||
* lib/rdoc/markup*: Renamespace from SM::SimpleMarkup to
|
||||
RDoc::Markup.
|
||||
|
||||
Mon Jan 14 10:45:45 2008 Martin Duerst <duerst@it.aoyama.ac.jp>
|
||||
|
||||
* enc/ascii.c: Exchanged order of arguments for one ENC_ALIAS
|
||||
|
|
51
bin/rdoc
51
bin/rdoc
|
@ -8,59 +8,14 @@
|
|||
#
|
||||
# $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.exist?(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 Interrupt
|
||||
$stderr.puts
|
||||
$stderr.puts "Interrupted"
|
||||
rescue RDoc::Error => e
|
||||
$stderr.puts e.message
|
||||
exit 1
|
||||
|
|
|
@ -64,6 +64,7 @@ profiler.rb
|
|||
pstore.rb
|
||||
racc
|
||||
rational.rb
|
||||
rdoc.rb
|
||||
rdoc
|
||||
readbytes.rb
|
||||
resolv-replace.rb
|
||||
|
|
18
lib/rdoc.rb
18
lib/rdoc.rb
|
@ -3,6 +3,13 @@
|
|||
|
||||
module RDoc
|
||||
|
||||
##
|
||||
# Exception thrown by any rdoc error.
|
||||
|
||||
class Error < RuntimeError; end
|
||||
|
||||
RDocError = Error # :nodoc:
|
||||
|
||||
##
|
||||
# RDoc version you are using
|
||||
|
||||
|
@ -14,5 +21,16 @@ module RDoc
|
|||
|
||||
DOT_DOC_FILENAME = ".document"
|
||||
|
||||
GENERAL_MODIFIERS = %w[nodoc].freeze
|
||||
|
||||
CLASS_MODIFIERS = GENERAL_MODIFIERS
|
||||
|
||||
ATTR_MODIFIERS = GENERAL_MODIFIERS
|
||||
|
||||
CONSTANT_MODIFIERS = GENERAL_MODIFIERS
|
||||
|
||||
METHOD_MODIFIERS = GENERAL_MODIFIERS +
|
||||
%w[arg args yield yields notnew not-new not_new doc]
|
||||
|
||||
end
|
||||
|
||||
|
|
613
lib/rdoc/README
613
lib/rdoc/README
|
@ -1,38 +1,34 @@
|
|||
= 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'.
|
||||
This package contains RDoc and RDoc::Markup. RDoc is an application that
|
||||
produces documentation for one or more Ruby source files. We work similarly to
|
||||
JavaDoc, parsing the source, and extracting the definition for classes,
|
||||
modules, and methods (along with includes and requires). We associate with
|
||||
these optional documentation contained in the immediately preceding comment
|
||||
block, and then render the result using a pluggable output formatter.
|
||||
RDoc::Markup is a library that converts plain text into various output formats.
|
||||
The markup library is used to interpret the comment blocks that RDoc uses to
|
||||
document methods, classes, and so on.
|
||||
|
||||
== Roadmap
|
||||
|
||||
* If you want to use Rdoc to create documentation for your Ruby source
|
||||
files, read on.
|
||||
* If you want to include extensions written in C, see rdoc/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 use RDoc to create documentation for your Ruby source files,
|
||||
read on.
|
||||
* If you want to include extensions written in C, see RDoc::C_Parser
|
||||
* For information on the various markups available in comment blocks, see
|
||||
RDoc::Markup.
|
||||
* If you want to drive RDoc programatically, see RDoc::RDoc.
|
||||
* If you want to use the library to format text blocks into HTML, have a look
|
||||
at RDoc::Markup.
|
||||
* If you want to try writing your own HTML output template, see
|
||||
RDoc::Page.
|
||||
RDoc::Generator::HTML
|
||||
|
||||
== Summary
|
||||
|
||||
Once installed, you can create documentation using the 'rdoc' command
|
||||
(the command is 'rdoc.bat' under Windows)
|
||||
|
||||
% rdoc [options] [names...]
|
||||
% rdoc [options] [names...]
|
||||
|
||||
Type "rdoc --help" for an up-to-date option summary.
|
||||
|
||||
|
@ -42,24 +38,170 @@ 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
|
||||
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
|
||||
index page contain the documentation for the primary file. In our
|
||||
case, we could type
|
||||
|
||||
% rdoc --main rdoc/rdoc.rb
|
||||
% rdoc --main rdoc.rb
|
||||
|
||||
You'll find information on the various formatting tricks you can use
|
||||
in comment blocks in the documentation this generates.
|
||||
|
||||
RDoc uses file extensions to determine how to process each file. File
|
||||
names ending <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.
|
||||
RDoc uses file extensions to determine how to process each file. File names
|
||||
ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files
|
||||
ending +.c+ are parsed as C files. All other files are assumed to
|
||||
contain just Markup-style markup (with or without leading '#' comment markers).
|
||||
If directory names are passed to RDoc, they are scanned recursively for C and
|
||||
Ruby source files only.
|
||||
|
||||
= Markup
|
||||
|
||||
For information on how to make lists, hyperlinks, & etc. with RDoc, see
|
||||
RDoc::Markup.
|
||||
|
||||
Comment blocks can be written fairly naturally, either using '#' on successive
|
||||
lines of the comment, or by including the comment in an =begin/=end block. If
|
||||
you use the latter form, the =begin line must be flagged with an RDoc tag:
|
||||
|
||||
=begin rdoc
|
||||
Documentation to be processed by RDoc.
|
||||
|
||||
...
|
||||
=end
|
||||
|
||||
RDoc stops processing comments if it finds a comment line containing '+#--+'.
|
||||
This can be used to separate external from internal comments, or to stop a
|
||||
comment being associated with a method, class, or module. Commenting can be
|
||||
turned back on with a line that starts '+#+++'.
|
||||
|
||||
##
|
||||
# Extract the age and calculate the date-of-birth.
|
||||
#--
|
||||
# FIXME: fails if the birthday falls on February 29th
|
||||
#++
|
||||
# The DOB is returned as a Time object.
|
||||
|
||||
def get_dob(person)
|
||||
# ...
|
||||
end
|
||||
|
||||
Names of classes, source files, and any method names containing an underscore
|
||||
or preceded by a hash character are automatically hyperlinked from comment text
|
||||
to their description.
|
||||
|
||||
Method parameter lists are extracted and displayed with the method description.
|
||||
If a method calls +yield+, then the parameters passed to yield will also be
|
||||
displayed:
|
||||
|
||||
def fred
|
||||
...
|
||||
yield line, address
|
||||
|
||||
This will get documented as:
|
||||
|
||||
fred() { |line, address| ... }
|
||||
|
||||
You can override this using a comment containing ':yields: ...' immediately
|
||||
after the method definition
|
||||
|
||||
def fred # :yields: index, position
|
||||
# ...
|
||||
|
||||
yield line, address
|
||||
|
||||
which will get documented as
|
||||
|
||||
fred() { |index, position| ... }
|
||||
|
||||
+:yields:+ is an example of a documentation directive. These appear immediately
|
||||
after the start of the document element they are modifying.
|
||||
|
||||
== Directives
|
||||
|
||||
[+:nodoc:+ / +:nodoc:+ all]
|
||||
Don't include this element in the documentation. For classes
|
||||
and modules, the methods, aliases, constants, and attributes
|
||||
directly within the affected class or module will also be
|
||||
omitted. By default, though, modules and classes within that
|
||||
class of module _will_ be documented. This is turned off by
|
||||
adding the +all+ modifier.
|
||||
|
||||
module MyModule # :nodoc:
|
||||
class Input
|
||||
end
|
||||
end
|
||||
|
||||
module OtherModule # :nodoc: all
|
||||
class Output
|
||||
end
|
||||
end
|
||||
|
||||
In the above code, only class +MyModule::Input+ will be documented.
|
||||
|
||||
[+:doc:+]
|
||||
Force a method or attribute to be documented even if it wouldn't otherwise
|
||||
be. Useful if, for example, you want to include documentation of a
|
||||
particular private method.
|
||||
|
||||
[+:notnew:+]
|
||||
Only applicable to the +initialize+ instance method. Normally RDoc assumes
|
||||
that the documentation and parameters for #initialize are actually for the
|
||||
::new method, and so fakes out a ::new for the class. The :notnew: modifier
|
||||
stops this. Remember that #initialize is protected, so you won't see the
|
||||
documentation unless you use the -a command line option.
|
||||
|
||||
Comment blocks can contain other directives:
|
||||
|
||||
[+:section: title+]
|
||||
Starts a new section in the output. The title following +:section:+ is used
|
||||
as the section heading, and the remainder of the comment containing the
|
||||
section is used as introductory text. Subsequent methods, aliases,
|
||||
attributes, and classes will be documented in this section. A :section:
|
||||
comment block may have one or more lines before the :section: directive.
|
||||
These will be removed, and any identical lines at the end of the block are
|
||||
also removed. This allows you to add visual cues such as:
|
||||
|
||||
# ----------------------------------------
|
||||
# :section: My Section
|
||||
# This is the section that I wrote.
|
||||
# See it glisten in the noon-day sun.
|
||||
# ----------------------------------------
|
||||
|
||||
[+:call-seq:+]
|
||||
Lines up to the next blank line in the comment are treated as the method's
|
||||
calling sequence, overriding the default parsing of method parameters and
|
||||
yield arguments.
|
||||
|
||||
[+:include:+ _filename_]
|
||||
Include the contents of the named file at this point. The file will be
|
||||
searched for in the directories listed by the +--include+ option, or in the
|
||||
current directory by default. The contents of the file will be shifted to
|
||||
have the same indentation as the ':' at the start of the :include: directive.
|
||||
|
||||
[+:title:+ _text_]
|
||||
Sets the title for the document. Equivalent to the --title command line
|
||||
parameter. (The command line parameter overrides any :title: directive in
|
||||
the source).
|
||||
|
||||
[+:enddoc:+]
|
||||
Document nothing further at the current level.
|
||||
|
||||
[+:main:+ _name_]
|
||||
Equivalent to the --main command line parameter.
|
||||
|
||||
[+:stopdoc:+ / +:startdoc:+]
|
||||
Stop and start adding new documentation elements to the current container.
|
||||
For example, if a class has a number of constants that you don't want to
|
||||
document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
|
||||
last. If you don't specifiy a +:startdoc:+ by the end of the container,
|
||||
disables documentation for the entire class or module.
|
||||
|
||||
= Other stuff
|
||||
|
||||
Author:: Dave Thomas <dave@pragmaticprogrammer.com>
|
||||
|
||||
== Credits
|
||||
|
||||
|
@ -82,408 +224,9 @@ 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_]
|
||||
the class of module _name_ will appear on the index page. If you
|
||||
want to set a particular file as a main page (a README, for
|
||||
example) simply specifiy its name as the first on the command
|
||||
line.
|
||||
|
||||
[<tt>--merge</tt>]
|
||||
when generating _ri_ output, if classes being processed already
|
||||
exist in the destination directory, merge in the current details
|
||||
rather than overwrite them.
|
||||
|
||||
[<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>--ri</tt>, <tt>--ri-site</tt>, _and_ <tt>--ri-system</tt>]
|
||||
generate output than can be read by the _ri_ command-line tool.
|
||||
By default --ri places its output in ~/.rdoc, --ri-site in
|
||||
$datadir/ri/<ver>/site, and --ri-system in
|
||||
$datadir/ri/<ver>/system. All can be overridden with a subsequent
|
||||
--op option. All default directories are in ri's default search
|
||||
path.
|
||||
|
||||
[<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/generator/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, either using '#' on
|
||||
successive lines of the comment, or by including the comment in
|
||||
an =begin/=end block. If you use the latter form, the =begin line
|
||||
must be flagged with an RDoc tag:
|
||||
|
||||
=begin rdoc
|
||||
Documentation to
|
||||
be processed by RDoc.
|
||||
=end
|
||||
|
||||
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. If <tt>label</tt> contains multiple words,
|
||||
put it in braces: <em>{multi word label}[</em>url<em>]</em>.
|
||||
|
||||
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>:section: title</tt>]
|
||||
Starts a new section in the output. The title following
|
||||
<tt>:section:</tt> is used as the section heading, and the
|
||||
remainder of the comment containing the section is used as
|
||||
introductory text. Subsequent methods, aliases, attributes,
|
||||
and classes will be documented in this section. A :section:
|
||||
comment block may have one or more lines before the :section:
|
||||
directive. These will be removed, and any identical lines at
|
||||
the end of the block are also removed. This allows you to add
|
||||
visual cues such as
|
||||
|
||||
# ----------------------------------------
|
||||
# :section: My Section
|
||||
# This is the section that I wrote.
|
||||
# See it glisten in the noon-day sun.
|
||||
# ----------------------------------------
|
||||
|
||||
[<tt>call-seq:</tt>]
|
||||
lines up to the next blank line in the comment are treated as
|
||||
the method's calling sequence, overriding the
|
||||
default parsing of method parameters and yield arguments.
|
||||
|
||||
[<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.
|
||||
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.
|
||||
|
||||
|
|
|
@ -458,7 +458,7 @@ module RDoc
|
|||
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# A TopLevel context is a source file
|
||||
|
||||
class TopLevel < Context
|
||||
|
@ -470,7 +470,7 @@ module RDoc
|
|||
@@all_classes = {}
|
||||
@@all_modules = {}
|
||||
|
||||
def TopLevel::reset
|
||||
def self.reset
|
||||
@@all_classes = {}
|
||||
@@all_modules = {}
|
||||
end
|
||||
|
@ -488,14 +488,15 @@ module RDoc
|
|||
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.
|
||||
##
|
||||
# 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_RDOC
|
||||
else
|
||||
|
@ -504,23 +505,29 @@ module RDoc
|
|||
else
|
||||
all = @@all_classes
|
||||
end
|
||||
|
||||
cls = all[name]
|
||||
|
||||
if !cls
|
||||
cls = class_type.new(name, superclass)
|
||||
all[name] = cls unless @done_documenting
|
||||
all[name] = cls unless @done_documenting
|
||||
end
|
||||
puts "Adding class/module #{name} to #@name" if $DEBUG_RDOC
|
||||
|
||||
puts "Adding class/module #{name} to #{@name}" if $DEBUG_RDOC
|
||||
|
||||
collection[name] = cls unless @done_documenting
|
||||
|
||||
cls.parent = self
|
||||
end
|
||||
|
||||
cls
|
||||
end
|
||||
|
||||
def TopLevel.all_classes_and_modules
|
||||
def self.all_classes_and_modules
|
||||
@@all_classes.values + @@all_modules.values
|
||||
end
|
||||
|
||||
def TopLevel.find_class_named(name)
|
||||
def self.find_class_named(name)
|
||||
@@all_classes.each_value do |c|
|
||||
res = c.find_class_named(name)
|
||||
return res if res
|
||||
|
@ -538,12 +545,13 @@ module RDoc
|
|||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Find a named module
|
||||
|
||||
def find_module_named(name)
|
||||
find_class_or_module_named(name) || find_enclosing_module_named(name)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# ClassModule is the base class for objects representing either a
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'cgi'
|
||||
require 'rdoc'
|
||||
require 'rdoc/options'
|
||||
require 'rdoc/markup/simple_markup'
|
||||
require 'rdoc/markup'
|
||||
require 'rdoc/template'
|
||||
|
||||
module RDoc::Generator
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'fileutils'
|
||||
|
||||
require 'rdoc/generator'
|
||||
require 'rdoc/markup/simple_markup/to_html'
|
||||
require 'rdoc/markup/to_html'
|
||||
|
||||
module RDoc::Generator
|
||||
|
||||
|
@ -34,12 +34,11 @@ module RDoc::Generator
|
|||
end
|
||||
|
||||
##
|
||||
# Subclass of the SM::ToHtml class that supports looking
|
||||
# up words in the AllReferences list. Those that are
|
||||
# found (like AllReferences in this comment) will
|
||||
# be hyperlinked
|
||||
# Subclass of the RDoc::Markup::ToHtml class that supports looking up words
|
||||
# in the AllReferences list. Those that are found (like AllReferences in
|
||||
# this comment) will be hyperlinked
|
||||
|
||||
class HyperlinkHtml < SM::ToHtml
|
||||
class HyperlinkHtml < RDoc::Markup::ToHtml
|
||||
|
||||
##
|
||||
# We need to record the html path of our caller so we can generate
|
||||
|
@ -161,13 +160,13 @@ module RDoc::Generator
|
|||
|
||||
##
|
||||
# Convert a string in markup format into HTML. We keep a cached
|
||||
# SimpleMarkup object lying around after the first time we're
|
||||
# RDoc::Markup object lying around after the first time we're
|
||||
# called per object.
|
||||
|
||||
def markup(str, remove_para=false)
|
||||
return '' unless str
|
||||
unless defined? @markup
|
||||
@markup = SM::SimpleMarkup.new
|
||||
@markup = RDoc::Markup.new
|
||||
|
||||
# class names, variable names, or instance variables
|
||||
@markup.add_special(/(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'rdoc/generator'
|
||||
require 'rdoc/markup/simple_markup/to_flow'
|
||||
require 'rdoc/markup/to_flow'
|
||||
|
||||
require 'rdoc/ri/cache'
|
||||
require 'rdoc/ri/reader'
|
||||
|
@ -26,8 +26,8 @@ class RDoc::Generator::RI
|
|||
def initialize(options) #:not-new:
|
||||
@options = options
|
||||
@ri_writer = RDoc::RI::Writer.new "."
|
||||
@markup = SM::SimpleMarkup.new
|
||||
@to_flow = SM::ToFlow.new
|
||||
@markup = RDoc::Markup.new
|
||||
@to_flow = RDoc::Markup::ToFlow.new
|
||||
|
||||
@generated = {}
|
||||
end
|
||||
|
@ -38,7 +38,7 @@ class RDoc::Generator::RI
|
|||
|
||||
def generate(toplevels)
|
||||
RDoc::TopLevel.all_classes_and_modules.each do |cls|
|
||||
process_class(cls)
|
||||
process_class cls
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -58,6 +58,7 @@ class RDoc::Generator::RI
|
|||
cls_desc = RDoc::RI::ClassDescription.new
|
||||
cls_desc.superclass = cls.superclass
|
||||
end
|
||||
|
||||
cls_desc.name = cls.name
|
||||
cls_desc.full_name = cls.full_name
|
||||
cls_desc.comment = markup(cls.comment)
|
||||
|
|
|
@ -0,0 +1,466 @@
|
|||
require 'rdoc'
|
||||
|
||||
##
|
||||
# RDoc::Markup 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.
|
||||
#
|
||||
# RDoc::Markup itself does no output formatting: this is left to a different
|
||||
# set of classes.
|
||||
#
|
||||
# RDoc::Markup is extendable at runtime: you can add new markup elements to be
|
||||
# recognised in the documents that RDoc::Markup parses.
|
||||
#
|
||||
# RDoc::Markup is 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
|
||||
# RDoc::Markup could be the basis for formating RDoc style comment blocks,
|
||||
# Wiki entries, and online FAQs.
|
||||
#
|
||||
# = Basic Formatting
|
||||
#
|
||||
# * RDoc::Markup 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, RDoc::Markup
|
||||
# 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.
|
||||
#
|
||||
# * 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. If <tt>label</tt> contains multiple words,
|
||||
# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
|
||||
#
|
||||
# == Synopsis
|
||||
#
|
||||
# This code converts <tt>input_string</tt> to HTML. The conversion
|
||||
# takes place in the +convert+ method, so you can use the same
|
||||
# RDoc::Markup object to convert multiple input strings.
|
||||
#
|
||||
# require 'rdoc/markup'
|
||||
# require 'rdoc/markup/to_html'
|
||||
#
|
||||
# p = RDoc::Markup.new
|
||||
# h = RDoc::Markup::ToHtml.new
|
||||
#
|
||||
# puts p.convert(input_string, h)
|
||||
#
|
||||
# You can extend the RDoc::Markup 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'
|
||||
# require 'rdoc/markup/to_html'
|
||||
#
|
||||
# class WikiHtml < RDoc::Markup::ToHtml
|
||||
# def handle_special_WIKIWORD(special)
|
||||
# "<font color=red>" + special.text + "</font>"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# p = RDoc::Markup.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>"
|
||||
#
|
||||
#--
|
||||
# Author:: Dave Thomas, dave@pragmaticprogrammer.com
|
||||
# Version:: 0.0
|
||||
# License:: Ruby license
|
||||
|
||||
class RDoc::Markup
|
||||
|
||||
SPACE = ?\s
|
||||
|
||||
# List entries look like:
|
||||
# * text
|
||||
# 1. text
|
||||
# [label] text
|
||||
# label:: text
|
||||
#
|
||||
# Flag it as a list entry, and work out the indent for subsequent lines
|
||||
|
||||
SIMPLE_LIST_RE = /^(
|
||||
( \* (?# bullet)
|
||||
|- (?# bullet)
|
||||
|\d+\. (?# numbered )
|
||||
|[A-Za-z]\. (?# alphabetically numbered )
|
||||
)
|
||||
\s+
|
||||
)\S/x
|
||||
|
||||
LABEL_LIST_RE = /^(
|
||||
( \[.*?\] (?# labeled )
|
||||
|\S.*:: (?# note )
|
||||
)(?:\s+|$)
|
||||
)/x
|
||||
|
||||
##
|
||||
# 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 be attached to the label.
|
||||
#
|
||||
|
||||
def handled_labeled_list(line, level, margin, offset, prefix)
|
||||
prefix_length = prefix.length
|
||||
text = line.text
|
||||
flag = nil
|
||||
case prefix
|
||||
when /^\[/
|
||||
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.type == Line::HEADING ? line.param : line.level
|
||||
end
|
||||
end
|
||||
|
||||
block.normalize
|
||||
block
|
||||
end
|
||||
|
||||
##
|
||||
# For debugging, we allow access to our line contents as text.
|
||||
|
||||
def content
|
||||
@lines.as_text
|
||||
end
|
||||
public :content
|
||||
|
||||
##
|
||||
# For debugging, return the list of line types.
|
||||
|
||||
def get_line_types
|
||||
@lines.line_types
|
||||
end
|
||||
public :get_line_types
|
||||
|
||||
end
|
||||
|
||||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/markup/lines'
|
|
@ -1,2 +0,0 @@
|
|||
simple_markup
|
||||
simple_markup.rb
|
|
@ -1,6 +1,7 @@
|
|||
require 'rdoc/markup/simple_markup/lines.rb'
|
||||
require 'rdoc/markup'
|
||||
require 'rdoc/markup/lines'
|
||||
|
||||
module SM
|
||||
class RDoc::Markup
|
||||
|
||||
##
|
||||
# A Fragment is a chunk of text, subclassed as a paragraph, a list
|
||||
|
@ -119,7 +120,7 @@ module SM
|
|||
|
||||
##
|
||||
# Collect groups of lines together. Each group will end up containing a flow
|
||||
# of text
|
||||
# of text.
|
||||
|
||||
class LineCollection
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
module SM
|
||||
require 'rdoc/markup'
|
||||
|
||||
class RDoc::Markup
|
||||
|
||||
##
|
||||
# We manage a set of attributes. Each attribute has a symbol name and a bit
|
||||
# value
|
||||
# value.
|
||||
|
||||
class Attribute
|
||||
SPECIAL = 1
|
||||
|
@ -39,7 +41,7 @@ module SM
|
|||
|
||||
##
|
||||
# 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
|
||||
# attributes to turn on, and a bitmap of those to turn off.
|
||||
|
||||
AttrChanger = Struct.new(:turn_on, :turn_off)
|
||||
|
||||
|
@ -50,7 +52,7 @@ module SM
|
|||
end
|
||||
|
||||
##
|
||||
# An array of attributes which parallels the characters in a string
|
||||
# An array of attributes which parallels the characters in a string.
|
||||
|
||||
class AttrSpan
|
||||
def initialize(length)
|
||||
|
@ -84,12 +86,12 @@ module SM
|
|||
end
|
||||
|
||||
def to_s
|
||||
"Special: type=#{type}, name=#{SM::Attribute.as_string type}, text=#{text.dump}"
|
||||
"Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<SM::Special:0x%x @type=%p, name=%p @text=%p>" % [
|
||||
object_id, @type, SM::Attribute.as_string(type), text.dump]
|
||||
"#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
|
||||
object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module SM
|
||||
class RDoc::Markup
|
||||
|
||||
##
|
||||
# We store the lines we're working on as objects of class Line. These
|
||||
|
@ -14,7 +14,7 @@ module SM
|
|||
RULE = :RULE
|
||||
PARAGRAPH = :PARAGRAPH
|
||||
VERBATIM = :VERBATIM
|
||||
|
||||
|
||||
# line type
|
||||
attr_accessor :type
|
||||
|
||||
|
@ -36,7 +36,6 @@ module SM
|
|||
|
||||
# true if this line has been deleted from the list of lines
|
||||
attr_accessor :deleted
|
||||
|
||||
|
||||
def initialize(text)
|
||||
@text = text.dup
|
||||
|
@ -69,7 +68,6 @@ module SM
|
|||
|
||||
##
|
||||
# Strip off the leading margin
|
||||
#
|
||||
|
||||
def strip_leading(size)
|
||||
if @text.size > size
|
||||
|
@ -85,7 +83,7 @@ module SM
|
|||
end
|
||||
|
||||
##
|
||||
# A container for all the lines
|
||||
# A container for all the lines.
|
||||
|
||||
class Lines
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
require 'rdoc/markup'
|
||||
|
||||
##
|
||||
# Handle common directives that can occur in a block of text:
|
||||
#
|
||||
# : include : filename
|
||||
|
||||
class RDoc::Markup::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
|
||||
prefix = $1
|
||||
directive = $2.downcase
|
||||
param = $3
|
||||
|
||||
case directive
|
||||
when "include"
|
||||
filename = param.split[0]
|
||||
include_file(filename, prefix)
|
||||
|
||||
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) then
|
||||
content = File.open(full_name) {|f| f.read}
|
||||
# strip leading '#'s, but only if all lines start with them
|
||||
if content =~ /^[^#]/
|
||||
content.gsub(/^/, indent)
|
||||
else
|
||||
content.gsub(/^#?/, indent)
|
||||
end
|
||||
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
|
||||
|
|
@ -1,463 +0,0 @@
|
|||
require 'rdoc/markup/simple_markup/fragments'
|
||||
require 'rdoc/markup/simple_markup/lines.rb'
|
||||
|
||||
##
|
||||
# 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
|
||||
|
||||
module SM
|
||||
|
||||
# == 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>"
|
||||
|
||||
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+|$)
|
||||
)/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 be attached to the label.
|
||||
#
|
||||
|
||||
def handled_labeled_list(line, level, margin, offset, prefix)
|
||||
prefix_length = prefix.length
|
||||
text = line.text
|
||||
flag = nil
|
||||
case prefix
|
||||
when /^\[/
|
||||
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.type == Line::HEADING ? line.param : line.level
|
||||
end
|
||||
end
|
||||
|
||||
block.normalize
|
||||
block
|
||||
end
|
||||
|
||||
##
|
||||
# For debugging, we allow access to our line contents as text.
|
||||
|
||||
def content
|
||||
@lines.as_text
|
||||
end
|
||||
public :content
|
||||
|
||||
##
|
||||
# For debugging, return the list of line types.
|
||||
|
||||
def get_line_types
|
||||
@lines.line_types
|
||||
end
|
||||
public :get_line_types
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
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
|
||||
prefix = $1
|
||||
directive = $2.downcase
|
||||
param = $3
|
||||
|
||||
case directive
|
||||
when "include"
|
||||
filename = param.split[0]
|
||||
include_file(filename, prefix)
|
||||
|
||||
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}
|
||||
# strip leading '#'s, but only if all lines start with them
|
||||
if content =~ /^[^#]/
|
||||
content.gsub(/^/, indent)
|
||||
else
|
||||
content.gsub(/^#?/, indent)
|
||||
end
|
||||
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
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,332 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
require 'rdoc/markup/simple_markup/fragments'
|
||||
require 'rdoc/markup/simple_markup/inline'
|
||||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/markup/inline'
|
||||
require 'cgi'
|
||||
|
||||
module SM
|
||||
class RDoc::Markup
|
||||
|
||||
module Flow
|
||||
P = Struct.new(:body)
|
||||
|
@ -24,12 +24,12 @@ module SM
|
|||
|
||||
class ToFlow
|
||||
LIST_TYPE_TO_HTML = {
|
||||
SM::ListBase::BULLET => [ "<ul>", "</ul>" ],
|
||||
SM::ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
||||
SM::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
||||
SM::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
||||
SM::ListBase::LABELED => [ "<dl>", "</dl>" ],
|
||||
SM::ListBase::NOTE => [ "<table>", "</table>" ],
|
||||
RDoc::Markup::ListBase::BULLET => [ "<ul>", "</ul>" ],
|
||||
RDoc::Markup::ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::LABELED => [ "<dl>", "</dl>" ],
|
||||
RDoc::Markup::ListBase::NOTE => [ "<table>", "</table>" ],
|
||||
}
|
||||
|
||||
InlineTag = Struct.new(:bit, :on, :off)
|
||||
|
@ -43,9 +43,9 @@ module SM
|
|||
|
||||
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>"),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -54,7 +54,7 @@ module SM
|
|||
# end tags for flexibility
|
||||
|
||||
def add_tag(name, start, stop)
|
||||
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
|
||||
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
|
||||
end
|
||||
|
||||
##
|
|
@ -0,0 +1,286 @@
|
|||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/markup/inline'
|
||||
|
||||
require 'cgi'
|
||||
|
||||
class RDoc::Markup::ToHtml
|
||||
|
||||
LIST_TYPE_TO_HTML = {
|
||||
RDoc::Markup::ListBase::BULLET => [ "<ul>", "</ul>" ],
|
||||
RDoc::Markup::ListBase::NUMBER => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
|
||||
RDoc::Markup::ListBase::LABELED => [ "<dl>", "</dl>" ],
|
||||
RDoc::Markup::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(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# Add a new set of HTML tags for an attribute. We allow separate start and
|
||||
# end tags for flexibility.
|
||||
|
||||
def add_tag(name, start, stop)
|
||||
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
|
||||
end
|
||||
|
||||
##
|
||||
# Given an HTML tag, decorate it with class information and the like if
|
||||
# required. This is a no-op in the base class, but is overridden in HTML
|
||||
# output classes that implement style sheets.
|
||||
|
||||
def annotate(tag)
|
||||
tag
|
||||
end
|
||||
|
||||
##
|
||||
# Here's the client side of the visitor pattern
|
||||
|
||||
def start_accepting
|
||||
@res = ""
|
||||
@in_list_entry = []
|
||||
end
|
||||
|
||||
def end_accepting
|
||||
@res
|
||||
end
|
||||
|
||||
def accept_paragraph(am, fragment)
|
||||
@res << annotate("<p>") + "\n"
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt)))
|
||||
@res << annotate("</p>") + "\n"
|
||||
end
|
||||
|
||||
def accept_verbatim(am, fragment)
|
||||
@res << annotate("<pre>") + "\n"
|
||||
@res << CGI.escapeHTML(fragment.txt)
|
||||
@res << annotate("</pre>") << "\n"
|
||||
end
|
||||
|
||||
def accept_rule(am, fragment)
|
||||
size = fragment.param
|
||||
size = 10 if size > 10
|
||||
@res << "<hr size=\"#{size}\"></hr>"
|
||||
end
|
||||
|
||||
def accept_list_start(am, fragment)
|
||||
@res << html_list_name(fragment.type, true) << "\n"
|
||||
@in_list_entry.push false
|
||||
end
|
||||
|
||||
def accept_list_end(am, fragment)
|
||||
if tag = @in_list_entry.pop
|
||||
@res << annotate(tag) << "\n"
|
||||
end
|
||||
@res << html_list_name(fragment.type, false) << "\n"
|
||||
end
|
||||
|
||||
def accept_list_item(am, fragment)
|
||||
if tag = @in_list_entry.last
|
||||
@res << annotate(tag) << "\n"
|
||||
end
|
||||
@res << list_item_start(am, fragment)
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
|
||||
@in_list_entry[-1] = list_end_for(fragment.type)
|
||||
end
|
||||
|
||||
def accept_blank_line(am, fragment)
|
||||
# @res << annotate("<p />") << "\n"
|
||||
end
|
||||
|
||||
def accept_heading(am, fragment)
|
||||
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
|
||||
end
|
||||
|
||||
##
|
||||
# This is a higher speed (if messier) version of wrap
|
||||
|
||||
def wrap(txt, line_len = 76)
|
||||
res = ""
|
||||
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 RDoc::Markup::AttrChanger
|
||||
off_tags(res, item)
|
||||
on_tags(res, item)
|
||||
when RDoc::Markup::Special
|
||||
res << convert_special(item)
|
||||
else
|
||||
raise "Unknown flow element: #{item.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
##
|
||||
# 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
|
||||
RDoc::Markup::Attribute.each_name_of(special.type) do |name|
|
||||
method_name = "handle_special_#{name}"
|
||||
if self.respond_to? method_name
|
||||
special.text = send(method_name, special)
|
||||
handled = true
|
||||
end
|
||||
end
|
||||
raise "Unhandled special: #{special}" unless handled
|
||||
special.text
|
||||
end
|
||||
|
||||
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 RDoc::Markup::ListBase::BULLET, RDoc::Markup::ListBase::NUMBER then
|
||||
annotate("<li>")
|
||||
|
||||
when RDoc::Markup::ListBase::UPPERALPHA then
|
||||
annotate("<li type=\"A\">")
|
||||
|
||||
when RDoc::Markup::ListBase::LOWERALPHA then
|
||||
annotate("<li type=\"a\">")
|
||||
|
||||
when RDoc::Markup::ListBase::LABELED then
|
||||
annotate("<dt>") +
|
||||
convert_flow(am.flow(fragment.param)) +
|
||||
annotate("</dt>") +
|
||||
annotate("<dd>")
|
||||
|
||||
when RDoc::Markup::ListBase::NOTE then
|
||||
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 RDoc::Markup::ListBase::BULLET, RDoc::Markup::ListBase::NUMBER,
|
||||
RDoc::Markup::ListBase::UPPERALPHA,
|
||||
RDoc::Markup::ListBase::LOWERALPHA then
|
||||
"</li>"
|
||||
when RDoc::Markup::ListBase::LABELED then
|
||||
"</dd>"
|
||||
when RDoc::Markup::ListBase::NOTE then
|
||||
"</td></tr>"
|
||||
else
|
||||
raise "Invalid list type"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/markup/inline'
|
||||
|
||||
require 'cgi'
|
||||
|
||||
##
|
||||
# Convert SimpleMarkup to basic LaTeX report format.
|
||||
|
||||
class RDoc::Markup::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)
|
||||
RDoc::Markup::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(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
|
||||
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# Escape a LaTeX string
|
||||
|
||||
def escape(str)
|
||||
$stderr.print "FE: ", str
|
||||
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(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
|
||||
end
|
||||
|
||||
##
|
||||
# Here's the client side of the visitor pattern
|
||||
|
||||
def start_accepting
|
||||
@res = ""
|
||||
@in_list_entry = []
|
||||
end
|
||||
|
||||
def end_accepting
|
||||
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
|
||||
end
|
||||
|
||||
def accept_paragraph(am, fragment)
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt)))
|
||||
@res << "\n"
|
||||
end
|
||||
|
||||
def accept_verbatim(am, fragment)
|
||||
@res << "\n\\begin{code}\n"
|
||||
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
|
||||
@res << "\n\\end{code}\n\n"
|
||||
end
|
||||
|
||||
def accept_rule(am, fragment)
|
||||
size = fragment.param
|
||||
size = 10 if size > 10
|
||||
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
|
||||
end
|
||||
|
||||
def accept_list_start(am, fragment)
|
||||
@res << list_name(fragment.type, true) << "\n"
|
||||
@in_list_entry.push false
|
||||
end
|
||||
|
||||
def accept_list_end(am, fragment)
|
||||
if tag = @in_list_entry.pop
|
||||
@res << tag << "\n"
|
||||
end
|
||||
@res << list_name(fragment.type, false) << "\n"
|
||||
end
|
||||
|
||||
def accept_list_item(am, fragment)
|
||||
if tag = @in_list_entry.last
|
||||
@res << tag << "\n"
|
||||
end
|
||||
@res << list_item_start(am, fragment)
|
||||
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
|
||||
@in_list_entry[-1] = list_end_for(fragment.type)
|
||||
end
|
||||
|
||||
def accept_blank_line(am, fragment)
|
||||
# @res << "\n"
|
||||
end
|
||||
|
||||
def accept_heading(am, fragment)
|
||||
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
|
||||
end
|
||||
|
||||
##
|
||||
# This is a higher speed (if messier) version of wrap
|
||||
|
||||
def wrap(txt, line_len = 76)
|
||||
res = ""
|
||||
sp = 0
|
||||
ep = txt.length
|
||||
while sp < ep
|
||||
# scan back for a space
|
||||
p = sp + line_len - 1
|
||||
if p >= ep
|
||||
p = ep
|
||||
else
|
||||
while p > sp and txt[p] != ?\s
|
||||
p -= 1
|
||||
end
|
||||
if p <= sp
|
||||
p = sp + line_len
|
||||
while p < ep and txt[p] != ?\s
|
||||
p += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
res << txt[sp...p] << "\n"
|
||||
sp = p
|
||||
sp += 1 while sp < ep and txt[sp] == ?\s
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def on_tags(res, item)
|
||||
attr_mask = item.turn_on
|
||||
return if attr_mask.zero?
|
||||
|
||||
@attr_tags.each do |tag|
|
||||
if attr_mask & tag.bit != 0
|
||||
res << tag.on
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def off_tags(res, item)
|
||||
attr_mask = item.turn_off
|
||||
return if attr_mask.zero?
|
||||
|
||||
@attr_tags.reverse_each do |tag|
|
||||
if attr_mask & tag.bit != 0
|
||||
res << tag.off
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def convert_flow(flow)
|
||||
res = ""
|
||||
flow.each do |item|
|
||||
case item
|
||||
when String
|
||||
$stderr.puts "Converting '#{item}'"
|
||||
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
|
||||
|
||||
d
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,41 +1,40 @@
|
|||
# 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'
|
||||
require 'rdoc/code_objects'
|
||||
require 'rdoc/markup/preprocess'
|
||||
|
||||
##
|
||||
# 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"
|
||||
class RDoc::SimpleParser
|
||||
|
||||
module RDoc
|
||||
# See rdoc/parsers/parse_c.rb
|
||||
##
|
||||
# Prepare to parse a plain file
|
||||
|
||||
class SimpleParser
|
||||
|
||||
# prepare to parse a plain file
|
||||
def initialize(top_level, file_name, body, options, stats)
|
||||
|
||||
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 = remove_private_comments(@body)
|
||||
@top_level
|
||||
def initialize(top_level, file_name, body, options, stats)
|
||||
preprocess = RDoc::Markup::PreProcess.new(file_name, options.rdoc_include)
|
||||
|
||||
preprocess.handle(body) do |directive, param|
|
||||
warn "Unrecognized directive '#{directive}' in #{file_name}"
|
||||
end
|
||||
|
||||
def remove_private_comments(comment)
|
||||
comment.gsub(/^--.*?^\+\+/m, '').sub(/^--.*/m, '')
|
||||
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
|
||||
@top_level.comment = remove_private_comments(@body)
|
||||
@top_level
|
||||
end
|
||||
|
||||
def remove_private_comments(comment)
|
||||
comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'rdoc/parsers/parse_c.rb'
|
|||
require 'rdoc/parsers/parse_f95.rb'
|
||||
require 'rdoc/parsers/parse_simple.rb'
|
||||
|
||||
require 'rdoc/stats'
|
||||
require 'rdoc/options'
|
||||
|
||||
require 'rdoc/diagram'
|
||||
|
@ -15,31 +16,6 @@ require 'time'
|
|||
|
||||
module RDoc
|
||||
|
||||
##
|
||||
# Simple stats collector
|
||||
|
||||
class Stats
|
||||
attr_accessor :num_files, :num_classes, :num_modules, :num_methods
|
||||
def initialize
|
||||
@num_files = @num_classes = @num_modules = @num_methods = 0
|
||||
@start = Time.now
|
||||
end
|
||||
def print
|
||||
puts "Files: #@num_files"
|
||||
puts "Classes: #@num_classes"
|
||||
puts "Modules: #@num_modules"
|
||||
puts "Methods: #@num_methods"
|
||||
puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Exception thrown by any rdoc error.
|
||||
|
||||
class Error < RuntimeError; end
|
||||
|
||||
RDocError = Error # :nodoc:
|
||||
|
||||
##
|
||||
# Encapsulate the production of rdoc documentation. Basically
|
||||
# you can use this as you would invoke rdoc from the command
|
||||
|
@ -192,22 +168,27 @@ module RDoc
|
|||
# for .document files.
|
||||
|
||||
def list_files_in_directory(dir, options)
|
||||
normalized_file_list(options, Dir.glob(File.join(dir, "*")), false, options.exclude)
|
||||
files = Dir.glob File.join(dir, "*")
|
||||
|
||||
normalized_file_list options, files, false, options.exclude
|
||||
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, true)
|
||||
|
||||
return [] if file_list.empty?
|
||||
|
||||
file_info = []
|
||||
width = file_list.map { |name| name.length }.max + 1
|
||||
|
||||
file_list.each do |fn|
|
||||
$stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
|
||||
$stderr.printf("\n%*s: ", width, fn) unless options.quiet
|
||||
|
||||
content = File.open(fn, "r:ascii-8bit") {|f| f.read}
|
||||
if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
|
||||
|
@ -252,6 +233,7 @@ module RDoc
|
|||
unless options.all_one_file
|
||||
@last_created = setup_output_dir(options.op_dir, options.force_update)
|
||||
end
|
||||
|
||||
start_time = Time.now
|
||||
|
||||
file_info = parse_files(options)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'yaml'
|
||||
require 'rdoc/markup/simple_markup/fragments'
|
||||
require 'rdoc/markup/fragments'
|
||||
require 'rdoc/ri'
|
||||
|
||||
#--
|
||||
|
@ -91,7 +91,7 @@ class RDoc::RI::ModuleDescription < RDoc::RI::Description
|
|||
@comment = old.comment
|
||||
else
|
||||
unless old.comment.nil? or old.comment.empty? then
|
||||
@comment << SM::Flow::RULE.new
|
||||
@comment << RDoc::Markup::Flow::RULE.new
|
||||
@comment.concat old.comment
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,7 +26,7 @@ end
|
|||
|
||||
##
|
||||
# A paging display module. Uses the RDoc::RI::Formatter class to do the actual
|
||||
# presentation
|
||||
# presentation.
|
||||
|
||||
class RDoc::RI::DefaultDisplay
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ require 'rdoc/ri/paths'
|
|||
require 'rdoc/ri/formatter'
|
||||
require 'rdoc/ri/display'
|
||||
require 'fileutils'
|
||||
require 'rdoc/markup/simple_markup'
|
||||
require 'rdoc/markup/simple_markup/to_flow'
|
||||
require 'rdoc/markup'
|
||||
require 'rdoc/markup/to_flow'
|
||||
|
||||
class RDoc::RI::Driver
|
||||
|
||||
|
@ -222,7 +222,7 @@ Options may also be set in the 'RI' environment variable.
|
|||
return @class_cache if @class_cache
|
||||
|
||||
newest = map_dirs('created.rid', :all) do |f|
|
||||
File.mtime f if test ?f, f
|
||||
File.mtime f if test ?f, f
|
||||
end.max
|
||||
|
||||
up_to_date = (File.exist?(class_cache_file_path) and
|
||||
|
@ -341,7 +341,7 @@ Options may also be set in the 'RI' environment variable.
|
|||
end
|
||||
|
||||
def read_yaml(path)
|
||||
YAML.load File.read(path).gsub(/ \!ruby\/(object|struct):(RDoc|RI).*/, '')
|
||||
YAML.load File.read(path).gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '')
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -99,17 +99,17 @@ class RDoc::RI::Formatter
|
|||
def display_list(list)
|
||||
case list.type
|
||||
|
||||
when SM::ListBase::BULLET
|
||||
when RDoc::Markup::ListBase::BULLET
|
||||
prefixer = proc { |ignored| @indent + "* " }
|
||||
|
||||
when SM::ListBase::NUMBER,
|
||||
SM::ListBase::UPPERALPHA,
|
||||
SM::ListBase::LOWERALPHA
|
||||
when RDoc::Markup::ListBase::NUMBER,
|
||||
RDoc::Markup::ListBase::UPPERALPHA,
|
||||
RDoc::Markup::ListBase::LOWERALPHA
|
||||
|
||||
start = case list.type
|
||||
when SM::ListBase::NUMBER then 1
|
||||
when SM::ListBase::UPPERALPHA then 'A'
|
||||
when SM::ListBase::LOWERALPHA then 'a'
|
||||
when RDoc::Markup::ListBase::NUMBER then 1
|
||||
when RDoc::Markup::ListBase::UPPERALPHA then 'A'
|
||||
when RDoc::Markup::ListBase::LOWERALPHA then 'a'
|
||||
end
|
||||
prefixer = proc do |ignored|
|
||||
res = @indent + "#{start}.".ljust(4)
|
||||
|
@ -117,15 +117,15 @@ class RDoc::RI::Formatter
|
|||
res
|
||||
end
|
||||
|
||||
when SM::ListBase::LABELED
|
||||
when RDoc::Markup::ListBase::LABELED
|
||||
prefixer = proc do |li|
|
||||
li.label
|
||||
end
|
||||
|
||||
when SM::ListBase::NOTE
|
||||
when RDoc::Markup::ListBase::NOTE
|
||||
longest = 0
|
||||
list.contents.each do |item|
|
||||
if item.kind_of?(SM::Flow::LI) && item.label.length > longest
|
||||
if item.kind_of?(RDoc::Markup::Flow::LI) && item.label.length > longest
|
||||
longest = item.label.length
|
||||
end
|
||||
end
|
||||
|
@ -140,7 +140,7 @@ class RDoc::RI::Formatter
|
|||
end
|
||||
|
||||
list.contents.each do |item|
|
||||
if item.kind_of? SM::Flow::LI
|
||||
if item.kind_of? RDoc::Markup::Flow::LI
|
||||
prefix = prefixer.call(item)
|
||||
display_flow_item(item, prefix)
|
||||
else
|
||||
|
@ -153,20 +153,20 @@ class RDoc::RI::Formatter
|
|||
|
||||
def display_flow_item(item, prefix=@indent)
|
||||
case item
|
||||
when SM::Flow::P, SM::Flow::LI
|
||||
when RDoc::Markup::Flow::P, RDoc::Markup::Flow::LI
|
||||
wrap(conv_html(item.body), prefix)
|
||||
blankline
|
||||
|
||||
when SM::Flow::LIST
|
||||
when RDoc::Markup::Flow::LIST
|
||||
display_list(item)
|
||||
|
||||
when SM::Flow::VERB
|
||||
when RDoc::Markup::Flow::VERB
|
||||
display_verbatim_flow_item(item, @indent)
|
||||
|
||||
when SM::Flow::H
|
||||
when RDoc::Markup::Flow::H
|
||||
display_heading(conv_html(item.text), item.level, @indent)
|
||||
|
||||
when SM::Flow::RULE
|
||||
when RDoc::Markup::Flow::RULE
|
||||
draw_line
|
||||
|
||||
else
|
||||
|
@ -508,23 +508,23 @@ class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter
|
|||
|
||||
def display_list(list)
|
||||
case list.type
|
||||
when SM::ListBase::BULLET
|
||||
when RDoc::Markup::ListBase::BULLET
|
||||
list_type = "ul"
|
||||
prefixer = proc { |ignored| "<li>" }
|
||||
|
||||
when SM::ListBase::NUMBER,
|
||||
SM::ListBase::UPPERALPHA,
|
||||
SM::ListBase::LOWERALPHA
|
||||
when RDoc::Markup::ListBase::NUMBER,
|
||||
RDoc::Markup::ListBase::UPPERALPHA,
|
||||
RDoc::Markup::ListBase::LOWERALPHA
|
||||
list_type = "ol"
|
||||
prefixer = proc { |ignored| "<li>" }
|
||||
|
||||
when SM::ListBase::LABELED
|
||||
when RDoc::Markup::ListBase::LABELED
|
||||
list_type = "dl"
|
||||
prefixer = proc do |li|
|
||||
"<dt><b>" + escape(li.label) + "</b><dd>"
|
||||
end
|
||||
|
||||
when SM::ListBase::NOTE
|
||||
when RDoc::Markup::ListBase::NOTE
|
||||
list_type = "table"
|
||||
prefixer = proc do |li|
|
||||
%{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>}
|
||||
|
@ -535,7 +535,7 @@ class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter
|
|||
|
||||
print "<#{list_type}>"
|
||||
list.contents.each do |item|
|
||||
if item.kind_of? SM::Flow::LI
|
||||
if item.kind_of? RDoc::Markup::Flow::LI
|
||||
prefix = prefixer.call(item)
|
||||
print prefix
|
||||
display_flow_item(item, prefix)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'rdoc/ri'
|
||||
require 'rdoc/ri/descriptions'
|
||||
require 'rdoc/ri/writer'
|
||||
require 'rdoc/markup/simple_markup/to_flow'
|
||||
require 'rdoc/markup/to_flow'
|
||||
|
||||
class RDoc::RI::Reader
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
require 'rdoc'
|
||||
|
||||
##
|
||||
# Simple stats collector
|
||||
|
||||
class RDoc::Stats
|
||||
|
||||
attr_accessor :num_files, :num_classes, :num_modules, :num_methods
|
||||
|
||||
def initialize
|
||||
@num_files = @num_classes = @num_modules = @num_methods = 0
|
||||
@start = Time.now
|
||||
end
|
||||
|
||||
def print
|
||||
puts "Files: #@num_files"
|
||||
puts "Classes: #@num_classes"
|
||||
puts "Modules: #@num_modules"
|
||||
puts "Methods: #@num_methods"
|
||||
puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
Загрузка…
Ссылка в новой задаче