summaryrefslogtreecommitdiff
path: root/lib/rdoc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rdoc')
-rw-r--r--lib/rdoc/.document1
-rw-r--r--lib/rdoc/README489
-rw-r--r--lib/rdoc/alias.rb112
-rw-r--r--lib/rdoc/anon_class.rb11
-rw-r--r--lib/rdoc/any_method.rb316
-rw-r--r--lib/rdoc/attr.rb176
-rw-r--r--lib/rdoc/class_module.rb802
-rw-r--r--lib/rdoc/code_object.rb421
-rw-r--r--lib/rdoc/code_objects.rb767
-rw-r--r--lib/rdoc/comment.rb239
-rw-r--r--lib/rdoc/constant.rb187
-rw-r--r--lib/rdoc/context.rb1235
-rw-r--r--lib/rdoc/context/section.rb245
-rw-r--r--lib/rdoc/cross_reference.rb184
-rw-r--r--lib/rdoc/diagram.rb335
-rw-r--r--lib/rdoc/dot/dot.rb255
-rw-r--r--lib/rdoc/encoding.rb130
-rw-r--r--lib/rdoc/erb_partial.rb19
-rw-r--r--lib/rdoc/erbio.rb38
-rw-r--r--lib/rdoc/extend.rb10
-rw-r--r--lib/rdoc/generator.rb51
-rw-r--r--lib/rdoc/generator/darkfish.rb786
-rw-r--r--lib/rdoc/generator/json_index.rb297
-rw-r--r--lib/rdoc/generator/markup.rb170
-rw-r--r--lib/rdoc/generator/pot.rb98
-rw-r--r--lib/rdoc/generator/pot/message_extractor.rb68
-rw-r--r--lib/rdoc/generator/pot/po.rb84
-rw-r--r--lib/rdoc/generator/pot/po_entry.rb141
-rw-r--r--lib/rdoc/generator/ri.rb31
-rw-r--r--lib/rdoc/generator/template/darkfish/.document0
-rw-r--r--lib/rdoc/generator/template/darkfish/_footer.rhtml5
-rw-r--r--lib/rdoc/generator/template/darkfish/_head.rhtml23
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml19
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml9
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml15
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml9
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml15
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml15
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml12
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml11
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml12
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml11
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml14
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml11
-rw-r--r--lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml18
-rw-r--r--lib/rdoc/generator/template/darkfish/class.rhtml172
-rw-r--r--lib/rdoc/generator/template/darkfish/css/fonts.css167
-rw-r--r--lib/rdoc/generator/template/darkfish/css/rdoc.css611
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttfbin0 -> 94668 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttfbin0 -> 94196 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttfbin0 -> 96184 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttfbin0 -> 95316 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttfbin0 -> 71200 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttfbin0 -> 71692 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/add.pngbin0 -> 733 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/arrow_up.pngbin0 -> 372 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/brick.pngbin0 -> 452 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/brick_link.pngbin0 -> 764 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/bug.pngbin0 -> 774 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/bullet_black.pngbin0 -> 211 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.pngbin0 -> 207 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.pngbin0 -> 209 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/date.pngbin0 -> 626 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/delete.pngbin0 -> 715 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/find.pngbin0 -> 659 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/loadingAnimation.gifbin0 -> 5886 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/macFFBgHack.pngbin0 -> 207 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/package.pngbin0 -> 853 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/page_green.pngbin0 -> 621 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/page_white_text.pngbin0 -> 342 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/page_white_width.pngbin0 -> 309 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/plugin.pngbin0 -> 591 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/ruby.pngbin0 -> 592 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/tag_blue.pngbin0 -> 1880 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/tag_green.pngbin0 -> 613 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/transparent.pngbin0 -> 97 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/wrench.pngbin0 -> 610 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/wrench_orange.pngbin0 -> 584 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/images/zoom.pngbin0 -> 692 bytes
-rw-r--r--lib/rdoc/generator/template/darkfish/index.rhtml23
-rw-r--r--lib/rdoc/generator/template/darkfish/js/darkfish.js84
-rw-r--r--lib/rdoc/generator/template/darkfish/js/search.js110
-rw-r--r--lib/rdoc/generator/template/darkfish/page.rhtml18
-rw-r--r--lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml18
-rw-r--r--lib/rdoc/generator/template/darkfish/servlet_root.rhtml63
-rw-r--r--lib/rdoc/generator/template/darkfish/table_of_contents.rhtml58
-rw-r--r--lib/rdoc/generator/template/json_index/.document1
-rw-r--r--lib/rdoc/generator/template/json_index/js/navigation.js106
-rw-r--r--lib/rdoc/generator/template/json_index/js/searcher.js229
-rw-r--r--lib/rdoc/generators/chm_generator.rb112
-rw-r--r--lib/rdoc/generators/html_generator.rb1509
-rw-r--r--lib/rdoc/generators/ri_generator.rb268
-rw-r--r--lib/rdoc/generators/template/chm/chm.rb87
-rw-r--r--lib/rdoc/generators/template/html/hefss.rb418
-rw-r--r--lib/rdoc/generators/template/html/html.rb711
-rw-r--r--lib/rdoc/generators/template/html/kilmer.rb435
-rw-r--r--lib/rdoc/generators/template/html/old_html.rb728
-rw-r--r--lib/rdoc/generators/template/html/one_page_html.rb122
-rw-r--r--lib/rdoc/generators/template/xml/rdf.rb112
-rw-r--r--lib/rdoc/generators/template/xml/xml.rb112
-rw-r--r--lib/rdoc/generators/xml_generator.rb130
-rw-r--r--lib/rdoc/ghost_method.rb7
-rw-r--r--lib/rdoc/i18n.rb10
-rw-r--r--lib/rdoc/i18n/locale.rb102
-rw-r--r--lib/rdoc/i18n/text.rb126
-rw-r--r--lib/rdoc/include.rb10
-rw-r--r--lib/rdoc/known_classes.rb73
-rw-r--r--lib/rdoc/markdown.rb16286
-rw-r--r--lib/rdoc/markdown/entities.rb2132
-rw-r--r--lib/rdoc/markdown/literals.rb416
-rw-r--r--lib/rdoc/markup.rb870
-rw-r--r--lib/rdoc/markup/.document2
-rw-r--r--lib/rdoc/markup/attr_changer.rb23
-rw-r--r--lib/rdoc/markup/attr_span.rb30
-rw-r--r--lib/rdoc/markup/attribute_manager.rb344
-rw-r--r--lib/rdoc/markup/attributes.rb71
-rw-r--r--lib/rdoc/markup/blank_line.rb28
-rw-r--r--lib/rdoc/markup/block_quote.rb15
-rw-r--r--lib/rdoc/markup/document.rb165
-rw-r--r--lib/rdoc/markup/formatter.rb265
-rw-r--r--lib/rdoc/markup/formatter_test_case.rb764
-rw-r--r--lib/rdoc/markup/hard_break.rb32
-rw-r--r--lib/rdoc/markup/heading.rb79
-rw-r--r--lib/rdoc/markup/include.rb43
-rw-r--r--lib/rdoc/markup/indented_paragraph.rb48
-rw-r--r--lib/rdoc/markup/inline.rb2
-rw-r--r--lib/rdoc/markup/list.rb102
-rw-r--r--lib/rdoc/markup/list_item.rb100
-rw-r--r--lib/rdoc/markup/paragraph.rb29
-rw-r--r--lib/rdoc/markup/parser.rb543
-rw-r--r--lib/rdoc/markup/pre_process.rb295
-rw-r--r--lib/rdoc/markup/raw.rb70
-rw-r--r--lib/rdoc/markup/rule.rb21
-rw-r--r--lib/rdoc/markup/sample/rdoc2latex.rb16
-rw-r--r--lib/rdoc/markup/sample/sample.rb42
-rw-r--r--lib/rdoc/markup/simple_markup.rb476
-rw-r--r--lib/rdoc/markup/simple_markup/fragments.rb328
-rw-r--r--lib/rdoc/markup/simple_markup/inline.rb340
-rw-r--r--lib/rdoc/markup/simple_markup/lines.rb151
-rw-r--r--lib/rdoc/markup/simple_markup/preprocess.rb73
-rw-r--r--lib/rdoc/markup/simple_markup/to_flow.rb188
-rw-r--r--lib/rdoc/markup/simple_markup/to_html.rb289
-rw-r--r--lib/rdoc/markup/simple_markup/to_latex.rb333
-rw-r--r--lib/rdoc/markup/special.rb41
-rw-r--r--lib/rdoc/markup/test/AllTests.rb2
-rw-r--r--lib/rdoc/markup/test/TestInline.rb154
-rw-r--r--lib/rdoc/markup/test/TestParse.rb503
-rw-r--r--lib/rdoc/markup/text_formatter_test_case.rb115
-rw-r--r--lib/rdoc/markup/to_ansi.rb94
-rw-r--r--lib/rdoc/markup/to_bs.rb77
-rw-r--r--lib/rdoc/markup/to_html.rb404
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb161
-rw-r--r--lib/rdoc/markup/to_html_snippet.rb285
-rw-r--r--lib/rdoc/markup/to_joined_paragraph.rb46
-rw-r--r--lib/rdoc/markup/to_label.rb75
-rw-r--r--lib/rdoc/markup/to_markdown.rb192
-rw-r--r--lib/rdoc/markup/to_rdoc.rb334
-rw-r--r--lib/rdoc/markup/to_table_of_contents.rb88
-rw-r--r--lib/rdoc/markup/to_test.rb70
-rw-r--r--lib/rdoc/markup/to_tt_only.rb121
-rw-r--r--lib/rdoc/markup/verbatim.rb84
-rw-r--r--lib/rdoc/meta_method.rb7
-rw-r--r--lib/rdoc/method_attr.rb419
-rw-r--r--lib/rdoc/mixin.rb121
-rw-r--r--lib/rdoc/normal_class.rb93
-rw-r--r--lib/rdoc/normal_module.rb74
-rw-r--r--lib/rdoc/options.rb1630
-rw-r--r--lib/rdoc/parser.rb277
-rw-r--r--lib/rdoc/parser/c.rb1268
-rw-r--r--lib/rdoc/parser/changelog.rb204
-rw-r--r--lib/rdoc/parser/markdown.rb24
-rw-r--r--lib/rdoc/parser/rd.rb23
-rw-r--r--lib/rdoc/parser/ripper_state_lex.rb605
-rw-r--r--lib/rdoc/parser/ruby.rb2232
-rw-r--r--lib/rdoc/parser/ruby_tools.rb159
-rw-r--r--lib/rdoc/parser/simple.rb61
-rw-r--r--lib/rdoc/parser/text.rb12
-rw-r--r--lib/rdoc/parsers/parse_c.rb697
-rw-r--r--lib/rdoc/parsers/parse_f95.rb1841
-rw-r--r--lib/rdoc/parsers/parse_rb.rb2605
-rw-r--r--lib/rdoc/parsers/parse_simple.rb41
-rw-r--r--lib/rdoc/parsers/parserfactory.rb99
-rw-r--r--lib/rdoc/rd.rb100
-rw-r--r--lib/rdoc/rd/block_parser.rb1055
-rw-r--r--lib/rdoc/rd/inline.rb72
-rw-r--r--lib/rdoc/rd/inline_parser.rb1207
-rw-r--r--lib/rdoc/rdoc.gemspec63
-rw-r--r--lib/rdoc/rdoc.rb725
-rw-r--r--lib/rdoc/require.rb52
-rw-r--r--lib/rdoc/ri.rb21
-rw-r--r--lib/rdoc/ri/driver.rb1547
-rw-r--r--lib/rdoc/ri/formatter.rb6
-rw-r--r--lib/rdoc/ri/paths.rb185
-rw-r--r--lib/rdoc/ri/ri_cache.rb187
-rw-r--r--lib/rdoc/ri/ri_descriptions.rb154
-rw-r--r--lib/rdoc/ri/ri_display.rb255
-rw-r--r--lib/rdoc/ri/ri_driver.rb143
-rw-r--r--lib/rdoc/ri/ri_formatter.rb674
-rw-r--r--lib/rdoc/ri/ri_options.rb313
-rw-r--r--lib/rdoc/ri/ri_paths.rb80
-rw-r--r--lib/rdoc/ri/ri_reader.rb100
-rw-r--r--lib/rdoc/ri/ri_util.rb75
-rw-r--r--lib/rdoc/ri/ri_writer.rb62
-rw-r--r--lib/rdoc/ri/store.rb7
-rw-r--r--lib/rdoc/ri/task.rb71
-rw-r--r--lib/rdoc/rubygems_hook.rb246
-rw-r--r--lib/rdoc/servlet.rb442
-rw-r--r--lib/rdoc/single_class.rb26
-rw-r--r--lib/rdoc/stats.rb462
-rw-r--r--lib/rdoc/stats/normal.rb58
-rw-r--r--lib/rdoc/stats/quiet.rb60
-rw-r--r--lib/rdoc/stats/verbose.rb46
-rw-r--r--lib/rdoc/store.rb968
-rw-r--r--lib/rdoc/task.rb329
-rw-r--r--lib/rdoc/template.rb234
-rw-r--r--lib/rdoc/test_case.rb203
-rw-r--r--lib/rdoc/text.rb298
-rw-r--r--lib/rdoc/token_stream.rb114
-rw-r--r--lib/rdoc/tokenstream.rb25
-rw-r--r--lib/rdoc/tom_doc.rb258
-rw-r--r--lib/rdoc/top_level.rb283
-rw-r--r--lib/rdoc/usage.rb210
222 files changed, 48411 insertions, 17983 deletions
diff --git a/lib/rdoc/.document b/lib/rdoc/.document
new file mode 100644
index 0000000000..41333c6411
--- /dev/null
+++ b/lib/rdoc/.document
@@ -0,0 +1 @@
+*.rb
diff --git a/lib/rdoc/README b/lib/rdoc/README
deleted file mode 100644
index 89ea0fbd3f..0000000000
--- a/lib/rdoc/README
+++ /dev/null
@@ -1,489 +0,0 @@
-= RDOC - Ruby Documentation System
-
-This package contains Rdoc and SimpleMarkup. Rdoc is an application
-that produces documentation for one or more Ruby source files. We work
-similarly to JavaDoc, parsing the source, and extracting the
-definition for classes, modules, and methods (along with includes and
-requires). We associate with these optional documentation contained
-in the immediately preceding comment block, and then render the result
-using a pluggable output formatter. (Currently, HTML is the only
-supported format. Markup is a library that converts plain text into
-various output formats. The Markup library is used to interpret the
-comment blocks that Rdoc uses to document methods, classes, and so on.
-
-This library contains two packages, rdoc itself and a text markup
-library, 'markup'.
-
-== Roadmap
-
-* If you want to use Rdoc to create documentation for your Ruby source
- files, read on.
-* If you want to include extensions written in C, see rdoc/parsers/parse_c.rb.
-* For information on the various markups available in comment
- blocks, see markup/simple_markup.rb.
-* If you want to drive Rdoc programatically, see RDoc::RDoc.
-* If you want to use the library to format text blocks into HTML,
- have a look at SM::SimpleMarkup.
-* If you want to try writing your own HTML output template, see
- RDoc::Page.
-
-== Summary
-
-Once installed, you can create documentation using the 'rdoc' command
-(the command is 'rdoc.bat' under Windows)
-
- % rdoc [options] [names...]
-
-Type "rdoc --help" for an up-to-date option summary.
-
-A typical use might be to generate documentation for a package of Ruby
-source (such as rdoc itself).
-
- % rdoc
-
-This command generates documentation for all the Ruby and C source
-files in and below the current directory. These will be stored in a
-documentation tree starting in the subdirectory 'doc'.
-
-You can make this slightly more useful for your readers by having the
-index page contain the documentation for the primary file. In our
-case, we could type
-
- % rdoc --main rdoc/rdoc.rb
-
-You'll find information on the various formatting tricks you can use
-in comment blocks in the documentation this generates.
-
-RDoc uses file extensions to determine how to process each file. File
-names ending <tt>.rb</tt> and <tt>.rbw</tt> are assumed to be Ruby
-source. Files ending <tt>.c</tt> are parsed as C files. All other
-files are assumed to contain just SimpleMarkup-style markup (with or
-without leading '#' comment markers). If directory names are passed to
-RDoc, they are scanned recursively for C and Ruby source files only.
-
-== Credits
-
-* The Ruby parser in rdoc/parse.rb is based heavily on the outstanding
- work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby
- parser for irb and the rtags package.
-
-* Code to diagram classes and modules was written by Sergey A Yanovitsky
- (Jah) of Enticla.
-
-* Charset patch from MoonWolf.
-
-* Rich Kilmer wrote the kilmer.rb output template.
-
-* Dan Brickley led the design of the RDF format.
-
-== License
-
-RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It
-is free software, and may be redistributed under the terms specified
-in the README file of the Ruby distribution.
-
-= Usage
-
-RDoc is invoked from the command line using:
-
- % rdoc <options> [name...]
-
-Files are parsed, and the information they contain collected, before
-any output is produced. This allows cross references between all files
-to be resolved. If a name is a directory, it is traversed. If no
-names are specified, all Ruby files in the current directory (and
-subdirectories) are processed.
-
-Options are:
-
-[<tt>--accessor</tt> <i>name[,name...]</i>]
- specifies the name(s) of additional methods that should be treated
- as if they were <tt>attr_</tt><i>xxx</i> methods. Specifying
- "--accessor db_opt" means lines such as
-
- db_opt :name, :age
-
- will get parsed and displayed in the documentation. Each name may have an
- optional "=flagtext" appended, in which case the given flagtext will appear
- where (for example) the 'rw' appears for attr_accessor.
-
-[<tt>--all</tt>]
- include protected and private methods in the output (by default
- only public methods are included)
-
-[<tt>--charset</tt> _charset_]
- Set the character set for the generated HTML.
-
-[<tt>--diagram</tt>]
- include diagrams showing modules and classes. This is currently
- an experimental feature, and may not be supported by all output
- templates. You need dot V1.8.6 or later to use the --diagram
- option correctly (http://www.research.att.com/sw/tools/graphviz/).
-
-[<tt>--exclude</tt> <i>pattern</i>]
- exclude files and directories matching this pattern from processing
-
-[<tt>--extension</tt> <i>new=old</i>]
- treat files ending <i>.new</i> as if they ended
- <i>.old</i>. Saying '--extension cgi=rb' causes RDoc to treat .cgi
- files as Ruby source.
-
-[<tt>fileboxes</tt>]
- Classes are put in boxes which represents files, where these
- classes reside. Classes shared between more than one file are
- shown with list of files that sharing them. Silently discarded if
- --diagram is not given Experimental.
-
-[<tt>--fmt</tt> _fmt_]
- generate output in a particular format.
-
-[<tt>--help</tt>]
- generate a usage summary.
-
-[<tt>--help-output</tt>]
- explain the various output options.
-
-[<tt>--image-format</tt> <i>gif/png/jpg/jpeg</i>]
- sets output image format for diagrams. Can be png, gif, jpeg,
- jpg. If this option is omitted, png is used. Requires --diagram.
-
-[<tt>--include</tt> <i>dir,...</i>]
- specify one or more directories to be searched when satisfying
- :+include+: directives. Multiple <tt>--include</tt> options may be
- given. The directory containing the file currently being processed
- is always searched.
-
-[<tt>--inline-source</tt>]
- By default, the source code of methods is shown in a popup. With
- this option, it's displayed inline.
-
-[<tt>line-numbers</tt>]
- include line numbers in the source code
-
-[<tt>--main</tt> _name_]
- 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/generators/xxxx_template, where 'xxxx'
- depends on the output formatter.
-
-[<tt>--version</tt>]
- display RDoc's version
-
-[<tt>--webcvs</tt> _url_]
- Specify a URL for linking to a web frontend to CVS. If the URL
- contains a '\%s', the name of the current file will be
- substituted; if the URL doesn't contain a '\%s', the filename will
- be appended to it.
-
-= Example
-
-A typical small Ruby program commented using RDoc might be as follows. You
-can see the formatted result in EXAMPLE.rb and Anagram.
-
- :include: EXAMPLE.rb
-
-= Markup
-
-Comment blocks can be written fairly naturally, 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.
diff --git a/lib/rdoc/alias.rb b/lib/rdoc/alias.rb
new file mode 100644
index 0000000000..858e053049
--- /dev/null
+++ b/lib/rdoc/alias.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+##
+# Represent an alias, which is an old_name/new_name pair associated with a
+# particular context
+#--
+# TODO implement Alias as a proxy to a method/attribute, inheriting from
+# MethodAttr
+
+class RDoc::Alias < RDoc::CodeObject
+
+ ##
+ # Aliased method's name
+
+ attr_reader :new_name
+
+ alias name new_name
+
+ ##
+ # Aliasee method's name
+
+ attr_reader :old_name
+
+ ##
+ # Is this an alias declared in a singleton context?
+
+ attr_accessor :singleton
+
+ ##
+ # Source file token stream
+
+ attr_reader :text
+
+ ##
+ # Creates a new Alias with a token stream of +text+ that aliases +old_name+
+ # to +new_name+, has +comment+ and is a +singleton+ context.
+
+ def initialize(text, old_name, new_name, comment, singleton = false)
+ super()
+
+ @text = text
+ @singleton = singleton
+ @old_name = old_name
+ @new_name = new_name
+ self.comment = comment
+ end
+
+ ##
+ # Order by #singleton then #new_name
+
+ def <=>(other)
+ [@singleton ? 0 : 1, new_name] <=> [other.singleton ? 0 : 1, other.new_name]
+ end
+
+ ##
+ # HTML fragment reference for this alias
+
+ def aref
+ type = singleton ? 'c' : 'i'
+ "#alias-#{type}-#{html_name}"
+ end
+
+ ##
+ # Full old name including namespace
+
+ def full_old_name
+ @full_name || "#{parent.name}#{pretty_old_name}"
+ end
+
+ ##
+ # HTML id-friendly version of +#new_name+.
+
+ def html_name
+ CGI.escape(@new_name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '')
+ end
+
+ def inspect # :nodoc:
+ parent_name = parent ? parent.name : '(unknown)'
+ "#<%s:0x%x %s.alias_method %s, %s>" % [
+ self.class, object_id,
+ parent_name, @old_name, @new_name,
+ ]
+ end
+
+ ##
+ # '::' for the alias of a singleton method/attribute, '#' for instance-level.
+
+ def name_prefix
+ singleton ? '::' : '#'
+ end
+
+ ##
+ # Old name with prefix '::' or '#'.
+
+ def pretty_old_name
+ "#{singleton ? '::' : '#'}#{@old_name}"
+ end
+
+ ##
+ # New name with prefix '::' or '#'.
+
+ def pretty_new_name
+ "#{singleton ? '::' : '#'}#{@new_name}"
+ end
+
+ alias pretty_name pretty_new_name
+
+ def to_s # :nodoc:
+ "alias: #{self.new_name} -> #{self.pretty_old_name} in: #{parent}"
+ end
+
+end
+
diff --git a/lib/rdoc/anon_class.rb b/lib/rdoc/anon_class.rb
new file mode 100644
index 0000000000..d02a38c2cf
--- /dev/null
+++ b/lib/rdoc/anon_class.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+##
+# An anonymous class like:
+#
+# c = Class.new do end
+#
+# AnonClass is currently not used.
+
+class RDoc::AnonClass < RDoc::ClassModule
+end
+
diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb
new file mode 100644
index 0000000000..9b0d309653
--- /dev/null
+++ b/lib/rdoc/any_method.rb
@@ -0,0 +1,316 @@
+# frozen_string_literal: true
+##
+# AnyMethod is the base class for objects representing methods
+
+class RDoc::AnyMethod < RDoc::MethodAttr
+
+ ##
+ # 2::
+ # RDoc 4
+ # Added calls_super
+ # Added parent name and class
+ # Added section title
+ # 3::
+ # RDoc 4.1
+ # Added is_alias_for
+
+ MARSHAL_VERSION = 3 # :nodoc:
+
+ ##
+ # Don't rename \#initialize to \::new
+
+ attr_accessor :dont_rename_initialize
+
+ ##
+ # The C function that implements this method (if it was defined in a C file)
+
+ attr_accessor :c_function
+
+ ##
+ # Different ways to call this method
+
+ attr_reader :call_seq
+
+ ##
+ # Parameters for this method
+
+ attr_accessor :params
+
+ ##
+ # If true this method uses +super+ to call a superclass version
+
+ attr_accessor :calls_super
+
+ include RDoc::TokenStream
+
+ ##
+ # Creates a new AnyMethod with a token stream +text+ and +name+
+
+ def initialize text, name
+ super
+
+ @c_function = nil
+ @dont_rename_initialize = false
+ @token_stream = nil
+ @calls_super = false
+ @superclass_method = nil
+ end
+
+ ##
+ # Adds +an_alias+ as an alias for this method in +context+.
+
+ def add_alias an_alias, context = nil
+ method = self.class.new an_alias.text, an_alias.new_name
+
+ method.record_location an_alias.file
+ method.singleton = self.singleton
+ method.params = self.params
+ method.visibility = self.visibility
+ method.comment = an_alias.comment
+ method.is_alias_for = self
+ @aliases << method
+ context.add_method method if context
+ method
+ end
+
+ ##
+ # Prefix for +aref+ is 'method'.
+
+ def aref_prefix
+ 'method'
+ end
+
+ ##
+ # The call_seq or the param_seq with method name, if there is no call_seq.
+ #
+ # Use this for displaying a method's argument lists.
+
+ def arglists
+ if @call_seq then
+ @call_seq
+ elsif @params then
+ "#{name}#{param_seq}"
+ end
+ end
+
+ ##
+ # Sets the different ways you can call this method. If an empty +call_seq+
+ # is given nil is assumed.
+ #
+ # See also #param_seq
+
+ def call_seq= call_seq
+ return if call_seq.empty?
+
+ @call_seq = call_seq
+ end
+
+ ##
+ # Loads is_alias_for from the internal name. Returns nil if the alias
+ # cannot be found.
+
+ def is_alias_for # :nodoc:
+ case @is_alias_for
+ when RDoc::MethodAttr then
+ @is_alias_for
+ when Array then
+ return nil unless @store
+
+ klass_name, singleton, method_name = @is_alias_for
+
+ return nil unless klass = @store.find_class_or_module(klass_name)
+
+ @is_alias_for = klass.find_method method_name, singleton
+ end
+ end
+
+ ##
+ # Dumps this AnyMethod for use by ri. See also #marshal_load
+
+ def marshal_dump
+ aliases = @aliases.map do |a|
+ [a.name, parse(a.comment)]
+ end
+
+ is_alias_for = [
+ @is_alias_for.parent.full_name,
+ @is_alias_for.singleton,
+ @is_alias_for.name
+ ] if @is_alias_for
+
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @singleton,
+ @visibility,
+ parse(@comment),
+ @call_seq,
+ @block_params,
+ aliases,
+ @params,
+ @file.relative_name,
+ @calls_super,
+ @parent.name,
+ @parent.class,
+ @section.title,
+ is_alias_for,
+ ]
+ end
+
+ ##
+ # Loads this AnyMethod from +array+. For a loaded AnyMethod the following
+ # methods will return cached values:
+ #
+ # * #full_name
+ # * #parent_name
+
+ def marshal_load array
+ initialize_visibility
+
+ @dont_rename_initialize = nil
+ @token_stream = nil
+ @aliases = []
+ @parent = nil
+ @parent_name = nil
+ @parent_class = nil
+ @section = nil
+ @file = nil
+
+ version = array[0]
+ @name = array[1]
+ @full_name = array[2]
+ @singleton = array[3]
+ @visibility = array[4]
+ @comment = array[5]
+ @call_seq = array[6]
+ @block_params = array[7]
+ # 8 handled below
+ @params = array[9]
+ # 10 handled below
+ @calls_super = array[11]
+ @parent_name = array[12]
+ @parent_title = array[13]
+ @section_title = array[14]
+ @is_alias_for = array[15]
+
+ array[8].each do |new_name, comment|
+ add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton)
+ end
+
+ @parent_name ||= if @full_name =~ /#/ then
+ $`
+ else
+ name = @full_name.split('::')
+ name.pop
+ name.join '::'
+ end
+
+ @file = RDoc::TopLevel.new array[10] if version > 0
+ end
+
+ ##
+ # Method name
+ #
+ # If the method has no assigned name, it extracts it from #call_seq.
+
+ def name
+ return @name if @name
+
+ @name =
+ @call_seq[/^.*?\.(\w+)/, 1] ||
+ @call_seq[/^.*?(\w+)/, 1] ||
+ @call_seq if @call_seq
+ end
+
+ ##
+ # A list of this method's method and yield parameters. +call-seq+ params
+ # are preferred over parsed method and block params.
+
+ def param_list
+ if @call_seq then
+ params = @call_seq.split("\n").last
+ params = params.sub(/.*?\((.*)\)/, '\1')
+ params = params.sub(/(\{|do)\s*\|([^|]*)\|.*/, ',\2')
+ elsif @params then
+ params = @params.sub(/\((.*)\)/, '\1')
+
+ params << ",#{@block_params}" if @block_params
+ elsif @block_params then
+ params = @block_params
+ else
+ return []
+ end
+
+ if @block_params then
+ # If this method has explicit block parameters, remove any explicit
+ # &block
+ params = params.sub(/,?\s*&\w+/, '')
+ else
+ params = params.sub(/\&(\w+)/, '\1')
+ end
+
+ params = params.gsub(/\s+/, '').split(',').reject(&:empty?)
+
+ params.map { |param| param.sub(/=.*/, '') }
+ end
+
+ ##
+ # Pretty parameter list for this method. If the method's parameters were
+ # given by +call-seq+ it is preferred over the parsed values.
+
+ def param_seq
+ if @call_seq then
+ params = @call_seq.split("\n").last
+ params = params.sub(/[^( ]+/, '')
+ params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2')
+ elsif @params then
+ params = @params.gsub(/\s*\#.*/, '')
+ params = params.tr_s("\n ", " ")
+ params = "(#{params})" unless params[0] == ?(
+ else
+ params = ''
+ end
+
+ if @block_params then
+ # If this method has explicit block parameters, remove any explicit
+ # &block
+ params = params.sub(/,?\s*&\w+/, '')
+
+ block = @block_params.tr_s("\n ", " ")
+ if block[0] == ?(
+ block = block.sub(/^\(/, '').sub(/\)/, '')
+ end
+ params << " { |#{block}| ... }"
+ end
+
+ params
+ end
+
+ ##
+ # Sets the store for this method and its referenced code objects.
+
+ def store= store
+ super
+
+ @file = @store.add_file @file.full_name if @file
+ end
+
+ ##
+ # For methods that +super+, find the superclass method that would be called.
+
+ def superclass_method
+ return unless @calls_super
+ return @superclass_method if @superclass_method
+
+ parent.each_ancestor do |ancestor|
+ if method = ancestor.method_list.find { |m| m.name == @name } then
+ @superclass_method = method
+ break
+ end
+ end
+
+ @superclass_method
+ end
+
+end
+
diff --git a/lib/rdoc/attr.rb b/lib/rdoc/attr.rb
new file mode 100644
index 0000000000..f780b3b976
--- /dev/null
+++ b/lib/rdoc/attr.rb
@@ -0,0 +1,176 @@
+# frozen_string_literal: true
+##
+# An attribute created by \#attr, \#attr_reader, \#attr_writer or
+# \#attr_accessor
+
+class RDoc::Attr < RDoc::MethodAttr
+
+ ##
+ # 3::
+ # RDoc 4
+ # Added parent name and class
+ # Added section title
+
+ MARSHAL_VERSION = 3 # :nodoc:
+
+ ##
+ # Is the attribute readable ('R'), writable ('W') or both ('RW')?
+
+ attr_accessor :rw
+
+ ##
+ # Creates a new Attr with body +text+, +name+, read/write status +rw+ and
+ # +comment+. +singleton+ marks this as a class attribute.
+
+ def initialize(text, name, rw, comment, singleton = false)
+ super text, name
+
+ @rw = rw
+ @singleton = singleton
+ self.comment = comment
+ end
+
+ ##
+ # Attributes are equal when their names, singleton and rw are identical
+
+ def == other
+ self.class == other.class and
+ self.name == other.name and
+ self.rw == other.rw and
+ self.singleton == other.singleton
+ end
+
+ ##
+ # Add +an_alias+ as an attribute in +context+.
+
+ def add_alias(an_alias, context)
+ new_attr = self.class.new(self.text, an_alias.new_name, self.rw,
+ self.comment, self.singleton)
+
+ new_attr.record_location an_alias.file
+ new_attr.visibility = self.visibility
+ new_attr.is_alias_for = self
+ @aliases << new_attr
+ context.add_attribute new_attr
+ new_attr
+ end
+
+ ##
+ # The #aref prefix for attributes
+
+ def aref_prefix
+ 'attribute'
+ end
+
+ ##
+ # Attributes never call super. See RDoc::AnyMethod#calls_super
+ #
+ # An RDoc::Attr can show up in the method list in some situations (see
+ # Gem::ConfigFile)
+
+ def calls_super # :nodoc:
+ false
+ end
+
+ ##
+ # Returns attr_reader, attr_writer or attr_accessor as appropriate.
+
+ def definition
+ case @rw
+ when 'RW' then 'attr_accessor'
+ when 'R' then 'attr_reader'
+ when 'W' then 'attr_writer'
+ end
+ end
+
+ def inspect # :nodoc:
+ alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
+ visibility = self.visibility
+ visibility = "forced #{visibility}" if force_documentation
+ "#<%s:0x%x %s %s (%s)%s>" % [
+ self.class, object_id,
+ full_name,
+ rw,
+ visibility,
+ alias_for,
+ ]
+ end
+
+ ##
+ # Dumps this Attr for use by ri. See also #marshal_load
+
+ def marshal_dump
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @rw,
+ @visibility,
+ parse(@comment),
+ singleton,
+ @file.relative_name,
+ @parent.full_name,
+ @parent.class,
+ @section.title
+ ]
+ end
+
+ ##
+ # Loads this Attr from +array+. For a loaded Attr the following
+ # methods will return cached values:
+ #
+ # * #full_name
+ # * #parent_name
+
+ def marshal_load array
+ initialize_visibility
+
+ @aliases = []
+ @parent = nil
+ @parent_name = nil
+ @parent_class = nil
+ @section = nil
+ @file = nil
+
+ version = array[0]
+ @name = array[1]
+ @full_name = array[2]
+ @rw = array[3]
+ @visibility = array[4]
+ @comment = array[5]
+ @singleton = array[6] || false # MARSHAL_VERSION == 0
+ # 7 handled below
+ @parent_name = array[8]
+ @parent_class = array[9]
+ @section_title = array[10]
+
+ @file = RDoc::TopLevel.new array[7] if version > 1
+
+ @parent_name ||= @full_name.split('#', 2).first
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[#{self.class.name} #{full_name} #{rw} #{visibility}", "]" do
+ unless comment.empty? then
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
+ end
+ end
+
+ def to_s # :nodoc:
+ "#{definition} #{name} in: #{parent}"
+ end
+
+ ##
+ # Attributes do not have token streams.
+ #
+ # An RDoc::Attr can show up in the method list in some situations (see
+ # Gem::ConfigFile)
+
+ def token_stream # :nodoc:
+ end
+
+end
+
diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb
new file mode 100644
index 0000000000..fdd56e236b
--- /dev/null
+++ b/lib/rdoc/class_module.rb
@@ -0,0 +1,802 @@
+# frozen_string_literal: true
+##
+# ClassModule is the base class for objects representing either a class or a
+# module.
+
+class RDoc::ClassModule < RDoc::Context
+
+ ##
+ # 1::
+ # RDoc 3.7
+ # * Added visibility, singleton and file to attributes
+ # * Added file to constants
+ # * Added file to includes
+ # * Added file to methods
+ # 2::
+ # RDoc 3.13
+ # * Added extends
+ # 3::
+ # RDoc 4.0
+ # * Added sections
+ # * Added in_files
+ # * Added parent name
+ # * Complete Constant dump
+
+ MARSHAL_VERSION = 3 # :nodoc:
+
+ ##
+ # Constants that are aliases for this class or module
+
+ attr_accessor :constant_aliases
+
+ ##
+ # Comment and the location it came from. Use #add_comment to add comments
+
+ attr_accessor :comment_location
+
+ attr_accessor :diagram # :nodoc:
+
+ ##
+ # Class or module this constant is an alias for
+
+ attr_accessor :is_alias_for
+
+ ##
+ # Return a RDoc::ClassModule of class +class_type+ that is a copy
+ # of module +module+. Used to promote modules to classes.
+ #--
+ # TODO move to RDoc::NormalClass (I think)
+
+ def self.from_module class_type, mod
+ klass = class_type.new mod.name
+
+ mod.comment_location.each do |comment, location|
+ klass.add_comment comment, location
+ end
+
+ klass.parent = mod.parent
+ klass.section = mod.section
+ klass.viewer = mod.viewer
+
+ klass.attributes.concat mod.attributes
+ klass.method_list.concat mod.method_list
+ klass.aliases.concat mod.aliases
+ klass.external_aliases.concat mod.external_aliases
+ klass.constants.concat mod.constants
+ klass.includes.concat mod.includes
+ klass.extends.concat mod.extends
+
+ klass.methods_hash.update mod.methods_hash
+ klass.constants_hash.update mod.constants_hash
+
+ klass.current_section = mod.current_section
+ klass.in_files.concat mod.in_files
+ klass.sections.concat mod.sections
+ klass.unmatched_alias_lists = mod.unmatched_alias_lists
+ klass.current_section = mod.current_section
+ klass.visibility = mod.visibility
+
+ klass.classes_hash.update mod.classes_hash
+ klass.modules_hash.update mod.modules_hash
+ klass.metadata.update mod.metadata
+
+ klass.document_self = mod.received_nodoc ? nil : mod.document_self
+ klass.document_children = mod.document_children
+ klass.force_documentation = mod.force_documentation
+ klass.done_documenting = mod.done_documenting
+
+ # update the parent of all children
+
+ (klass.attributes +
+ klass.method_list +
+ klass.aliases +
+ klass.external_aliases +
+ klass.constants +
+ klass.includes +
+ klass.extends +
+ klass.classes +
+ klass.modules).each do |obj|
+ obj.parent = klass
+ obj.full_name = nil
+ end
+
+ klass
+ end
+
+ ##
+ # Creates a new ClassModule with +name+ with optional +superclass+
+ #
+ # This is a constructor for subclasses, and must never be called directly.
+
+ def initialize(name, superclass = nil)
+ @constant_aliases = []
+ @diagram = nil
+ @is_alias_for = nil
+ @name = name
+ @superclass = superclass
+ @comment_location = [] # [[comment, location]]
+
+ super()
+ end
+
+ ##
+ # Adds +comment+ to this ClassModule's list of comments at +location+. This
+ # method is preferred over #comment= since it allows ri data to be updated
+ # across multiple runs.
+
+ def add_comment comment, location
+ return unless document_self
+
+ original = comment
+
+ comment = case comment
+ when RDoc::Comment then
+ comment.normalize
+ else
+ normalize_comment comment
+ end
+
+ if location.parser == RDoc::Parser::C
+ @comment_location.delete_if { |(_, l)| l == location }
+ end
+
+ @comment_location << [comment, location]
+
+ self.comment = original
+ end
+
+ def add_things my_things, other_things # :nodoc:
+ other_things.each do |group, things|
+ my_things[group].each { |thing| yield false, thing } if
+ my_things.include? group
+
+ things.each do |thing|
+ yield true, thing
+ end
+ end
+ end
+
+ ##
+ # Ancestors list for this ClassModule: the list of included modules
+ # (classes will add their superclass if any).
+ #
+ # Returns the included classes or modules, not the includes
+ # themselves. The returned values are either String or
+ # RDoc::NormalModule instances (see RDoc::Include#module).
+ #
+ # The values are returned in reverse order of their inclusion,
+ # which is the order suitable for searching methods/attributes
+ # in the ancestors. The superclass, if any, comes last.
+
+ def ancestors
+ includes.map { |i| i.module }.reverse
+ end
+
+ def aref_prefix # :nodoc:
+ raise NotImplementedError, "missing aref_prefix for #{self.class}"
+ end
+
+ ##
+ # HTML fragment reference for this module or class. See
+ # RDoc::NormalClass#aref and RDoc::NormalModule#aref
+
+ def aref
+ "#{aref_prefix}-#{full_name}"
+ end
+
+ ##
+ # Ancestors of this class or module only
+
+ alias direct_ancestors ancestors
+
+ ##
+ # Clears the comment. Used by the Ruby parser.
+
+ def clear_comment
+ @comment = ''
+ end
+
+ ##
+ # This method is deprecated, use #add_comment instead.
+ #
+ # Appends +comment+ to the current comment, but separated by a rule. Works
+ # more like <tt>+=</tt>.
+
+ def comment= comment # :nodoc:
+ comment = case comment
+ when RDoc::Comment then
+ comment.normalize
+ else
+ normalize_comment comment
+ end
+
+ comment = "#{@comment}\n---\n#{comment}" unless @comment.empty?
+
+ super comment
+ end
+
+ ##
+ # Prepares this ClassModule for use by a generator.
+ #
+ # See RDoc::Store#complete
+
+ def complete min_visibility
+ update_aliases
+ remove_nodoc_children
+ update_includes
+ remove_invisible min_visibility
+ end
+
+ ##
+ # Does this ClassModule or any of its methods have document_self set?
+
+ def document_self_or_methods
+ document_self || method_list.any?{ |m| m.document_self }
+ end
+
+ ##
+ # Does this class or module have a comment with content or is
+ # #received_nodoc true?
+
+ def documented?
+ return true if @received_nodoc
+ return false if @comment_location.empty?
+ @comment_location.any? { |comment, _| not comment.empty? }
+ end
+
+ ##
+ # Iterates the ancestors of this class or module for which an
+ # RDoc::ClassModule exists.
+
+ def each_ancestor # :yields: module
+ return enum_for __method__ unless block_given?
+
+ ancestors.each do |mod|
+ next if String === mod
+ next if self == mod
+ yield mod
+ end
+ end
+
+ ##
+ # Looks for a symbol in the #ancestors. See Context#find_local_symbol.
+
+ def find_ancestor_local_symbol symbol
+ each_ancestor do |m|
+ res = m.find_local_symbol(symbol)
+ return res if res
+ end
+
+ nil
+ end
+
+ ##
+ # Finds a class or module with +name+ in this namespace or its descendants
+
+ def find_class_named name
+ return self if full_name == name
+ return self if @name == name
+
+ @classes.values.find do |klass|
+ next if klass == self
+ klass.find_class_named name
+ end
+ end
+
+ ##
+ # Return the fully qualified name of this class or module
+
+ def full_name
+ @full_name ||= if RDoc::ClassModule === parent then
+ "#{parent.full_name}::#{@name}"
+ else
+ @name
+ end
+ end
+
+ ##
+ # TODO: filter included items by #display?
+
+ def marshal_dump # :nodoc:
+ attrs = attributes.sort.map do |attr|
+ next unless attr.display?
+ [ attr.name, attr.rw,
+ attr.visibility, attr.singleton, attr.file_name,
+ ]
+ end.compact
+
+ method_types = methods_by_type.map do |type, visibilities|
+ visibilities = visibilities.map do |visibility, methods|
+ method_names = methods.map do |method|
+ next unless method.display?
+ [method.name, method.file_name]
+ end.compact
+
+ [visibility, method_names.uniq]
+ end
+
+ [type, visibilities]
+ end
+
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @superclass,
+ parse(@comment_location),
+ attrs,
+ constants.select { |constant| constant.display? },
+ includes.map do |incl|
+ next unless incl.display?
+ [incl.name, parse(incl.comment), incl.file_name]
+ end.compact,
+ method_types,
+ extends.map do |ext|
+ next unless ext.display?
+ [ext.name, parse(ext.comment), ext.file_name]
+ end.compact,
+ @sections.values,
+ @in_files.map do |tl|
+ tl.relative_name
+ end,
+ parent.full_name,
+ parent.class,
+ ]
+ end
+
+ def marshal_load array # :nodoc:
+ initialize_visibility
+ initialize_methods_etc
+ @current_section = nil
+ @document_self = true
+ @done_documenting = false
+ @parent = nil
+ @temporary_section = nil
+ @visibility = nil
+ @classes = {}
+ @modules = {}
+
+ @name = array[1]
+ @full_name = array[2]
+ @superclass = array[3]
+ @comment = array[4]
+
+ @comment_location = if RDoc::Markup::Document === @comment.parts.first then
+ @comment
+ else
+ RDoc::Markup::Document.new @comment
+ end
+
+ array[5].each do |name, rw, visibility, singleton, file|
+ singleton ||= false
+ visibility ||= :public
+
+ attr = RDoc::Attr.new nil, name, rw, nil, singleton
+
+ add_attribute attr
+ attr.visibility = visibility
+ attr.record_location RDoc::TopLevel.new file
+ end
+
+ array[6].each do |constant, comment, file|
+ case constant
+ when RDoc::Constant then
+ add_constant constant
+ else
+ constant = add_constant RDoc::Constant.new(constant, nil, comment)
+ constant.record_location RDoc::TopLevel.new file
+ end
+ end
+
+ array[7].each do |name, comment, file|
+ incl = add_include RDoc::Include.new(name, comment)
+ incl.record_location RDoc::TopLevel.new file
+ end
+
+ array[8].each do |type, visibilities|
+ visibilities.each do |visibility, methods|
+ @visibility = visibility
+
+ methods.each do |name, file|
+ method = RDoc::AnyMethod.new nil, name
+ method.singleton = true if type == 'class'
+ method.record_location RDoc::TopLevel.new file
+ add_method method
+ end
+ end
+ end
+
+ array[9].each do |name, comment, file|
+ ext = add_extend RDoc::Extend.new(name, comment)
+ ext.record_location RDoc::TopLevel.new file
+ end if array[9] # Support Marshal version 1
+
+ sections = (array[10] || []).map do |section|
+ [section.title, section]
+ end
+
+ @sections = Hash[*sections.flatten]
+ @current_section = add_section nil
+
+ @in_files = []
+
+ (array[11] || []).each do |filename|
+ record_location RDoc::TopLevel.new filename
+ end
+
+ @parent_name = array[12]
+ @parent_class = array[13]
+ end
+
+ ##
+ # Merges +class_module+ into this ClassModule.
+ #
+ # The data in +class_module+ is preferred over the receiver.
+
+ def merge class_module
+ @parent = class_module.parent
+ @parent_name = class_module.parent_name
+
+ other_document = parse class_module.comment_location
+
+ if other_document then
+ document = parse @comment_location
+
+ document = document.merge other_document
+
+ @comment = @comment_location = document
+ end
+
+ cm = class_module
+ other_files = cm.in_files
+
+ merge_collections attributes, cm.attributes, other_files do |add, attr|
+ if add then
+ add_attribute attr
+ else
+ @attributes.delete attr
+ @methods_hash.delete attr.pretty_name
+ end
+ end
+
+ merge_collections constants, cm.constants, other_files do |add, const|
+ if add then
+ add_constant const
+ else
+ @constants.delete const
+ @constants_hash.delete const.name
+ end
+ end
+
+ merge_collections includes, cm.includes, other_files do |add, incl|
+ if add then
+ add_include incl
+ else
+ @includes.delete incl
+ end
+ end
+
+ @includes.uniq! # clean up
+
+ merge_collections extends, cm.extends, other_files do |add, ext|
+ if add then
+ add_extend ext
+ else
+ @extends.delete ext
+ end
+ end
+
+ @extends.uniq! # clean up
+
+ merge_collections method_list, cm.method_list, other_files do |add, meth|
+ if add then
+ add_method meth
+ else
+ @method_list.delete meth
+ @methods_hash.delete meth.pretty_name
+ end
+ end
+
+ merge_sections cm
+
+ self
+ end
+
+ ##
+ # Merges collection +mine+ with +other+ preferring other. +other_files+ is
+ # used to help determine which items should be deleted.
+ #
+ # Yields whether the item should be added or removed (true or false) and the
+ # item to be added or removed.
+ #
+ # merge_collections things, other.things, other.in_files do |add, thing|
+ # if add then
+ # # add the thing
+ # else
+ # # remove the thing
+ # end
+ # end
+
+ def merge_collections mine, other, other_files, &block # :nodoc:
+ my_things = mine. group_by { |thing| thing.file }
+ other_things = other.group_by { |thing| thing.file }
+
+ remove_things my_things, other_files, &block
+ add_things my_things, other_things, &block
+ end
+
+ ##
+ # Merges the comments in this ClassModule with the comments in the other
+ # ClassModule +cm+.
+
+ def merge_sections cm # :nodoc:
+ my_sections = sections.group_by { |section| section.title }
+ other_sections = cm.sections.group_by { |section| section.title }
+
+ other_files = cm.in_files
+
+ remove_things my_sections, other_files do |_, section|
+ @sections.delete section.title
+ end
+
+ other_sections.each do |group, sections|
+ if my_sections.include? group
+ my_sections[group].each do |my_section|
+ other_section = cm.sections_hash[group]
+
+ my_comments = my_section.comments
+ other_comments = other_section.comments
+
+ other_files = other_section.in_files
+
+ merge_collections my_comments, other_comments, other_files do |add, comment|
+ if add then
+ my_section.add_comment comment
+ else
+ my_section.remove_comment comment
+ end
+ end
+ end
+ else
+ sections.each do |section|
+ add_section group, section.comments
+ end
+ end
+ end
+ end
+
+ ##
+ # Does this object represent a module?
+
+ def module?
+ false
+ end
+
+ ##
+ # Allows overriding the initial name.
+ #
+ # Used for modules and classes that are constant aliases.
+
+ def name= new_name
+ @name = new_name
+ end
+
+ ##
+ # Parses +comment_location+ into an RDoc::Markup::Document composed of
+ # multiple RDoc::Markup::Documents with their file set.
+
+ def parse comment_location
+ case comment_location
+ when String then
+ super
+ when Array then
+ docs = comment_location.map do |comment, location|
+ doc = super comment
+ doc.file = location
+ doc
+ end
+
+ RDoc::Markup::Document.new(*docs)
+ when RDoc::Comment then
+ doc = super comment_location.text, comment_location.format
+ doc.file = comment_location.location
+ doc
+ when RDoc::Markup::Document then
+ return comment_location
+ else
+ raise ArgumentError, "unknown comment class #{comment_location.class}"
+ end
+ end
+
+ ##
+ # Path to this class or module for use with HTML generator output.
+
+ def path
+ http_url @store.rdoc.generator.class_dir
+ end
+
+ ##
+ # Name to use to generate the url:
+ # modules and classes that are aliases for another
+ # module or class return the name of the latter.
+
+ def name_for_path
+ is_alias_for ? is_alias_for.full_name : full_name
+ end
+
+ ##
+ # Returns the classes and modules that are not constants
+ # aliasing another class or module. For use by formatters
+ # only (caches its result).
+
+ def non_aliases
+ @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for }
+ end
+
+ ##
+ # Updates the child modules or classes of class/module +parent+ by
+ # deleting the ones that have been removed from the documentation.
+ #
+ # +parent_hash+ is either <tt>parent.modules_hash</tt> or
+ # <tt>parent.classes_hash</tt> and +all_hash+ is ::all_modules_hash or
+ # ::all_classes_hash.
+
+ def remove_nodoc_children
+ prefix = self.full_name + '::'
+
+ modules_hash.each_key do |name|
+ full_name = prefix + name
+ modules_hash.delete name unless @store.modules_hash[full_name]
+ end
+
+ classes_hash.each_key do |name|
+ full_name = prefix + name
+ classes_hash.delete name unless @store.classes_hash[full_name]
+ end
+ end
+
+ def remove_things my_things, other_files # :nodoc:
+ my_things.delete_if do |file, things|
+ next false unless other_files.include? file
+
+ things.each do |thing|
+ yield false, thing
+ end
+
+ true
+ end
+ end
+
+ ##
+ # Search record used by RDoc::Generator::JsonIndex
+
+ def search_record
+ [
+ name,
+ full_name,
+ full_name,
+ '',
+ path,
+ '',
+ snippet(@comment_location),
+ ]
+ end
+
+ ##
+ # Sets the store for this class or module and its contained code objects.
+
+ def store= store
+ super
+
+ @attributes .each do |attr| attr.store = store end
+ @constants .each do |const| const.store = store end
+ @includes .each do |incl| incl.store = store end
+ @extends .each do |ext| ext.store = store end
+ @method_list.each do |meth| meth.store = store end
+ end
+
+ ##
+ # Get the superclass of this class. Attempts to retrieve the superclass
+ # object, returns the name if it is not known.
+
+ def superclass
+ @store.find_class_named(@superclass) || @superclass
+ end
+
+ ##
+ # Set the superclass of this class to +superclass+
+
+ def superclass=(superclass)
+ raise NoMethodError, "#{full_name} is a module" if module?
+ @superclass = superclass
+ end
+
+ def to_s # :nodoc:
+ if is_alias_for then
+ "#{self.class.name} #{self.full_name} -> #{is_alias_for}"
+ else
+ super
+ end
+ end
+
+ ##
+ # 'module' or 'class'
+
+ def type
+ module? ? 'module' : 'class'
+ end
+
+ ##
+ # Updates the child modules & classes by replacing the ones that are
+ # aliases through a constant.
+ #
+ # The aliased module/class is replaced in the children and in
+ # RDoc::Store#modules_hash or RDoc::Store#classes_hash
+ # by a copy that has <tt>RDoc::ClassModule#is_alias_for</tt> set to
+ # the aliased module/class, and this copy is added to <tt>#aliases</tt>
+ # of the aliased module/class.
+ #
+ # Formatters can use the #non_aliases method to retrieve children that
+ # are not aliases, for instance to list the namespace content, since
+ # the aliased modules are included in the constants of the class/module,
+ # that are listed separately.
+
+ def update_aliases
+ constants.each do |const|
+ next unless cm = const.is_alias_for
+ cm_alias = cm.dup
+ cm_alias.name = const.name
+
+ # Don't move top-level aliases under Object, they look ugly there
+ unless RDoc::TopLevel === cm_alias.parent then
+ cm_alias.parent = self
+ cm_alias.full_name = nil # force update for new parent
+ end
+
+ cm_alias.aliases.clear
+ cm_alias.is_alias_for = cm
+
+ if cm.module? then
+ @store.modules_hash[cm_alias.full_name] = cm_alias
+ modules_hash[const.name] = cm_alias
+ else
+ @store.classes_hash[cm_alias.full_name] = cm_alias
+ classes_hash[const.name] = cm_alias
+ end
+
+ cm.aliases << cm_alias
+ end
+ end
+
+ ##
+ # Deletes from #includes those whose module has been removed from the
+ # documentation.
+ #--
+ # FIXME: includes are not reliably removed, see _possible_bug test case
+
+ def update_includes
+ includes.reject! do |include|
+ mod = include.module
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
+ end
+
+ includes.uniq!
+ end
+
+ ##
+ # Deletes from #extends those whose module has been removed from the
+ # documentation.
+ #--
+ # FIXME: like update_includes, extends are not reliably removed
+
+ def update_extends
+ extends.reject! do |ext|
+ mod = ext.module
+
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
+ end
+
+ extends.uniq!
+ end
+
+end
+
diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb
new file mode 100644
index 0000000000..aeb4b4762e
--- /dev/null
+++ b/lib/rdoc/code_object.rb
@@ -0,0 +1,421 @@
+# frozen_string_literal: true
+##
+# Base class for the RDoc code tree.
+#
+# We contain the common stuff for contexts (which are containers) and other
+# elements (methods, attributes and so on)
+#
+# Here's the tree of the CodeObject subclasses:
+#
+# * RDoc::Context
+# * RDoc::TopLevel
+# * RDoc::ClassModule
+# * RDoc::AnonClass (never used so far)
+# * RDoc::NormalClass
+# * RDoc::NormalModule
+# * RDoc::SingleClass
+# * RDoc::MethodAttr
+# * RDoc::Attr
+# * RDoc::AnyMethod
+# * RDoc::GhostMethod
+# * RDoc::MetaMethod
+# * RDoc::Alias
+# * RDoc::Constant
+# * RDoc::Mixin
+# * RDoc::Require
+# * RDoc::Include
+
+class RDoc::CodeObject
+
+ include RDoc::Text
+
+ ##
+ # Our comment
+
+ attr_reader :comment
+
+ ##
+ # Do we document our children?
+
+ attr_reader :document_children
+
+ ##
+ # Do we document ourselves?
+
+ attr_reader :document_self
+
+ ##
+ # Are we done documenting (ie, did we come across a :enddoc:)?
+
+ attr_reader :done_documenting
+
+ ##
+ # Which file this code object was defined in
+
+ attr_reader :file
+
+ ##
+ # Force documentation of this CodeObject
+
+ attr_reader :force_documentation
+
+ ##
+ # Line in #file where this CodeObject was defined
+
+ attr_accessor :line
+
+ ##
+ # Hash of arbitrary metadata for this CodeObject
+
+ attr_reader :metadata
+
+ ##
+ # Sets the parent CodeObject
+
+ attr_writer :parent
+
+ ##
+ # Did we ever receive a +:nodoc:+ directive?
+
+ attr_reader :received_nodoc
+
+ ##
+ # Set the section this CodeObject is in
+
+ attr_writer :section
+
+ ##
+ # The RDoc::Store for this object.
+
+ attr_reader :store
+
+ ##
+ # We are the model of the code, but we know that at some point we will be
+ # worked on by viewers. By implementing the Viewable protocol, viewers can
+ # associated themselves with these objects.
+
+ attr_accessor :viewer
+
+ ##
+ # Creates a new CodeObject that will document itself and its children
+
+ def initialize
+ @metadata = {}
+ @comment = ''
+ @parent = nil
+ @parent_name = nil # for loading
+ @parent_class = nil # for loading
+ @section = nil
+ @section_title = nil # for loading
+ @file = nil
+ @full_name = nil
+ @store = nil
+ @track_visibility = true
+
+ initialize_visibility
+ end
+
+ ##
+ # Initializes state for visibility of this CodeObject and its children.
+
+ def initialize_visibility # :nodoc:
+ @document_children = true
+ @document_self = true
+ @done_documenting = false
+ @force_documentation = false
+ @received_nodoc = false
+ @ignored = false
+ @suppressed = false
+ @track_visibility = true
+ end
+
+ ##
+ # Replaces our comment with +comment+, unless it is empty.
+
+ def comment=(comment)
+ @comment = case comment
+ when NilClass then ''
+ when RDoc::Markup::Document then comment
+ when RDoc::Comment then comment.normalize
+ else
+ if comment and not comment.empty? then
+ normalize_comment comment
+ else
+ # HACK correct fix is to have #initialize create @comment
+ # with the correct encoding
+ if String === @comment and @comment.empty? then
+ @comment = RDoc::Encoding.change_encoding @comment, comment.encoding
+ end
+ @comment
+ end
+ end
+ end
+
+ ##
+ # Should this CodeObject be displayed in output?
+ #
+ # A code object should be displayed if:
+ #
+ # * The item didn't have a nodoc or wasn't in a container that had nodoc
+ # * The item wasn't ignored
+ # * The item has documentation and was not suppressed
+
+ def display?
+ @document_self and not @ignored and
+ (documented? or not @suppressed)
+ end
+
+ ##
+ # Enables or disables documentation of this CodeObject's children unless it
+ # has been turned off by :enddoc:
+
+ def document_children=(document_children)
+ return unless @track_visibility
+
+ @document_children = document_children unless @done_documenting
+ end
+
+ ##
+ # Enables or disables documentation of this CodeObject unless it has been
+ # turned off by :enddoc:. If the argument is +nil+ it means the
+ # documentation is turned off by +:nodoc:+.
+
+ def document_self=(document_self)
+ return unless @track_visibility
+ return if @done_documenting
+
+ @document_self = document_self
+ @received_nodoc = true if document_self.nil?
+ end
+
+ ##
+ # Does this object have a comment with content or is #received_nodoc true?
+
+ def documented?
+ @received_nodoc or !@comment.empty?
+ end
+
+ ##
+ # Turns documentation on/off, and turns on/off #document_self
+ # and #document_children.
+ #
+ # Once documentation has been turned off (by +:enddoc:+),
+ # the object will refuse to turn #document_self or
+ # #document_children on, so +:doc:+ and +:start_doc:+ directives
+ # will have no effect in the current file.
+
+ def done_documenting=(value)
+ return unless @track_visibility
+ @done_documenting = value
+ @document_self = !value
+ @document_children = @document_self
+ end
+
+ ##
+ # Yields each parent of this CodeObject. See also
+ # RDoc::ClassModule#each_ancestor
+
+ def each_parent
+ code_object = self
+
+ while code_object = code_object.parent do
+ yield code_object
+ end
+
+ self
+ end
+
+ ##
+ # File name where this CodeObject was found.
+ #
+ # See also RDoc::Context#in_files
+
+ def file_name
+ return unless @file
+
+ @file.absolute_name
+ end
+
+ ##
+ # Force the documentation of this object unless documentation
+ # has been turned off by :enddoc:
+ #--
+ # HACK untested, was assigning to an ivar
+
+ def force_documentation=(value)
+ @force_documentation = value unless @done_documenting
+ end
+
+ ##
+ # Sets the full_name overriding any computed full name.
+ #
+ # Set to +nil+ to clear RDoc's cached value
+
+ def full_name= full_name
+ @full_name = full_name
+ end
+
+ ##
+ # Use this to ignore a CodeObject and all its children until found again
+ # (#record_location is called). An ignored item will not be displayed in
+ # documentation.
+ #
+ # See github issue #55
+ #
+ # The ignored status is temporary in order to allow implementation details
+ # to be hidden. At the end of processing a file RDoc allows all classes
+ # and modules to add new documentation to previously created classes.
+ #
+ # If a class was ignored (via stopdoc) then reopened later with additional
+ # documentation it should be displayed. If a class was ignored and never
+ # reopened it should not be displayed. The ignore flag allows this to
+ # occur.
+
+ def ignore
+ return unless @track_visibility
+
+ @ignored = true
+
+ stop_doc
+ end
+
+ ##
+ # Has this class been ignored?
+ #
+ # See also #ignore
+
+ def ignored?
+ @ignored
+ end
+
+ ##
+ # The options instance from the store this CodeObject is attached to, or a
+ # default options instance if the CodeObject is not attached.
+ #
+ # This is used by Text#snippet
+
+ def options
+ if @store and @store.rdoc then
+ @store.rdoc.options
+ else
+ RDoc::Options.new
+ end
+ end
+
+ ##
+ # Our parent CodeObject. The parent may be missing for classes loaded from
+ # legacy RI data stores.
+
+ def parent
+ return @parent if @parent
+ return nil unless @parent_name
+
+ if @parent_class == RDoc::TopLevel then
+ @parent = @store.add_file @parent_name
+ else
+ @parent = @store.find_class_or_module @parent_name
+
+ return @parent if @parent
+
+ begin
+ @parent = @store.load_class @parent_name
+ rescue RDoc::Store::MissingFileError
+ nil
+ end
+ end
+ end
+
+ ##
+ # File name of our parent
+
+ def parent_file_name
+ @parent ? @parent.base_name : '(unknown)'
+ end
+
+ ##
+ # Name of our parent
+
+ def parent_name
+ @parent ? @parent.full_name : '(unknown)'
+ end
+
+ ##
+ # Records the RDoc::TopLevel (file) where this code object was defined
+
+ def record_location top_level
+ @ignored = false
+ @suppressed = false
+ @file = top_level
+ end
+
+ ##
+ # The section this CodeObject is in. Sections allow grouping of constants,
+ # attributes and methods inside a class or module.
+
+ def section
+ return @section if @section
+
+ @section = parent.add_section @section_title if parent
+ end
+
+ ##
+ # Enable capture of documentation unless documentation has been
+ # turned off by :enddoc:
+
+ def start_doc
+ return if @done_documenting
+
+ @document_self = true
+ @document_children = true
+ @ignored = false
+ @suppressed = false
+ end
+
+ ##
+ # Disable capture of documentation
+
+ def stop_doc
+ return unless @track_visibility
+
+ @document_self = false
+ @document_children = false
+ end
+
+ ##
+ # Sets the +store+ that contains this CodeObject
+
+ def store= store
+ @store = store
+
+ return unless @track_visibility
+
+ if :nodoc == options.visibility then
+ initialize_visibility
+ @track_visibility = false
+ end
+ end
+
+ ##
+ # Use this to suppress a CodeObject and all its children until the next file
+ # it is seen in or documentation is discovered. A suppressed item with
+ # documentation will be displayed while an ignored item with documentation
+ # may not be displayed.
+
+ def suppress
+ return unless @track_visibility
+
+ @suppressed = true
+
+ stop_doc
+ end
+
+ ##
+ # Has this class been suppressed?
+ #
+ # See also #suppress
+
+ def suppressed?
+ @suppressed
+ end
+
+end
diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb
index d6c4f1bdb9..434a25ac7f 100644
--- a/lib/rdoc/code_objects.rb
+++ b/lib/rdoc/code_objects.rb
@@ -1,765 +1,6 @@
-# We represent the various high-level code constructs that appear
-# in Ruby programs: classes, modules, methods, and so on.
+# frozen_string_literal: true
+# This file was used to load all the RDoc::CodeObject subclasses at once. Now
+# autoload handles this.
-require 'rdoc/tokenstream'
+require 'rdoc'
-module RDoc
-
-
- # We contain the common stuff for contexts (which are containers)
- # and other elements (methods, attributes and so on)
- #
- class CodeObject
-
- attr_accessor :parent
-
- # We are the model of the code, but we know that at some point
- # we will be worked on by viewers. By implementing the Viewable
- # protocol, viewers can associated themselves with these objects.
-
- attr_accessor :viewer
-
- # are we done documenting (ie, did we come across a :enddoc:)?
-
- attr_accessor :done_documenting
-
- # Which section are we in
-
- attr_accessor :section
-
- # do we document ourselves?
-
- attr_reader :document_self
-
- def document_self=(val)
- @document_self = val
- if !val
- remove_methods_etc
- end
- end
-
- # set and cleared by :startdoc: and :enddoc:, this is used to toggle
- # the capturing of documentation
- def start_doc
- @document_self = true
- @document_children = true
- end
-
- def stop_doc
- @document_self = false
- @document_children = false
- end
-
- # do we document ourselves and our children
-
- attr_reader :document_children
-
- def document_children=(val)
- @document_children = val
- if !val
- remove_classes_and_modules
- end
- end
-
- # Do we _force_ documentation, even is we wouldn't normally show the entity
- attr_accessor :force_documentation
-
- # Default callbacks to nothing, but this is overridden for classes
- # and modules
- def remove_classes_and_modules
- end
-
- def remove_methods_etc
- end
-
- def initialize
- @document_self = true
- @document_children = true
- @force_documentation = false
- @done_documenting = false
- end
-
- # Access the code object's comment
- attr_reader :comment
-
- # Update the comment, but don't overwrite a real comment
- # with an empty one
- def comment=(comment)
- @comment = comment unless comment.empty?
- end
-
- # There's a wee trick we pull. Comment blocks can have directives that
- # override the stuff we extract during the parse. So, we have a special
- # class method, attr_overridable, that lets code objects list
- # those directives. Wehn a comment is assigned, we then extract
- # out any matching directives and update our object
-
- def CodeObject.attr_overridable(name, *aliases)
- @overridables ||= {}
-
- attr_accessor name
-
- aliases.unshift name
- aliases.each do |directive_name|
- @overridables[directive_name.to_s] = name
- end
- end
-
- end
-
- # A Context is something that can hold modules, classes, methods,
- # attributes, aliases, requires, and includes. Classes, modules, and
- # files are all Contexts.
-
- class Context < CodeObject
- attr_reader :name, :method_list, :attributes, :aliases, :constants
- attr_reader :requires, :includes, :in_files, :visibility
-
- attr_reader :sections
-
- class Section
- attr_reader :title, :comment, :sequence
-
- @@sequence = "SEC00000"
-
- def initialize(title, comment)
- @title = title
- @@sequence.succ!
- @sequence = @@sequence.dup
- set_comment(comment)
- end
-
- private
-
- # Set the comment for this section from the original comment block
- # If the first line contains :section:, strip it and use the rest. Otherwise
- # remove lines up to the line containing :section:, and look for
- # those lines again at the end and remove them. This lets us write
- #
- # # ---------------------
- # # :SECTION: The title
- # # The body
- # # ---------------------
-
- def set_comment(comment)
- return unless comment
-
- if comment =~ /^.*?:section:.*$/
- start = $`
- rest = $'
- if start.empty?
- @comment = rest
- else
- @comment = rest.sub(/#{start.chomp}\Z/, '')
- end
- else
- @comment = comment
- end
- @comment = nil if @comment.empty?
- end
- end
-
-
- def initialize
- super()
-
- @in_files = []
-
- @name ||= "unknown"
- @comment ||= ""
- @parent = nil
- @visibility = :public
-
- @current_section = Section.new(nil, nil)
- @sections = [ @current_section ]
-
- initialize_methods_etc
- initialize_classes_and_modules
- end
-
- # map the class hash to an array externally
- def classes
- @classes.values
- end
-
- # map the module hash to an array externally
- def modules
- @modules.values
- end
-
- # Change the default visibility for new methods
- def ongoing_visibility=(vis)
- @visibility = vis
- end
-
- # Given an array +methods+ of method names, set the
- # visibility of the corresponding AnyMethod object
-
- def set_visibility_for(methods, vis, singleton=false)
- count = 0
- @method_list.each do |m|
- if methods.include?(m.name) && m.singleton == singleton
- m.visibility = vis
- count += 1
- end
- end
-
- return if count == methods.size || singleton
-
- # perhaps we need to look at attributes
-
- @attributes.each do |a|
- if methods.include?(a.name)
- a.visibility = vis
- count += 1
- end
- end
- end
-
- # Record the file that we happen to find it in
- def record_location(toplevel)
- @in_files << toplevel unless @in_files.include?(toplevel)
- end
-
- # Return true if at least part of this thing was defined in +file+
- def defined_in?(file)
- @in_files.include?(file)
- end
-
- def add_class(class_type, name, superclass)
- add_class_or_module(@classes, class_type, name, superclass)
- end
-
- def add_module(class_type, name)
- add_class_or_module(@modules, class_type, name, nil)
- end
-
- def add_method(a_method)
- puts "Adding #@visibility method #{a_method.name} to #@name" if $DEBUG
- a_method.visibility = @visibility
- add_to(@method_list, a_method)
- end
-
- def add_attribute(an_attribute)
- add_to(@attributes, an_attribute)
- end
-
- def add_alias(an_alias)
- meth = find_instance_method_named(an_alias.old_name)
- if meth
- new_meth = AnyMethod.new(an_alias.text, an_alias.new_name)
- new_meth.is_alias_for = meth
- new_meth.singleton = meth.singleton
- new_meth.params = meth.params
- new_meth.comment = "Alias for \##{meth.name}"
- meth.add_alias(new_meth)
- add_method(new_meth)
- else
- add_to(@aliases, an_alias)
- end
- end
-
- def add_include(an_include)
- add_to(@includes, an_include)
- end
-
- def add_constant(const)
- add_to(@constants, const)
- end
-
- # Requires always get added to the top-level (file) context
- def add_require(a_require)
- if self.kind_of? TopLevel
- add_to(@requires, a_require)
- else
- parent.add_require(a_require)
- end
- end
-
- def add_class_or_module(collection, class_type, name, superclass=nil)
- cls = collection[name]
- if cls
- puts "Reusing class/module #{name}" if $DEBUG
- else
- cls = class_type.new(name, superclass)
- puts "Adding class/module #{name} to #@name" if $DEBUG
-# collection[name] = cls if @document_self && !@done_documenting
- collection[name] = cls if !@done_documenting
- cls.parent = self
- cls.section = @current_section
- end
- cls
- end
-
- def add_to(array, thing)
- array << thing if @document_self && !@done_documenting
- thing.parent = self
- thing.section = @current_section
- end
-
- # If a class's documentation is turned off after we've started
- # collecting methods etc., we need to remove the ones
- # we have
-
- def remove_methods_etc
- initialize_methods_etc
- end
-
- def initialize_methods_etc
- @method_list = []
- @attributes = []
- @aliases = []
- @requires = []
- @includes = []
- @constants = []
- end
-
- # and remove classes and modules when we see a :nodoc: all
- def remove_classes_and_modules
- initialize_classes_and_modules
- end
-
- def initialize_classes_and_modules
- @classes = {}
- @modules = {}
- end
-
- # Find a named module
- def find_module_named(name)
- return self if self.name == name
- res = @modules[name] || @classes[name]
- return res if res
- find_enclosing_module_named(name)
- end
-
- # find a module at a higher scope
- def find_enclosing_module_named(name)
- parent && parent.find_module_named(name)
- end
-
- # Iterate over all the classes and modules in
- # this object
-
- def each_classmodule
- @modules.each_value {|m| yield m}
- @classes.each_value {|c| yield c}
- end
-
- def each_method
- @method_list.each {|m| yield m}
- end
-
- def each_attribute
- @attributes.each {|a| yield a}
- end
-
- def each_constant
- @constants.each {|c| yield c}
- end
-
- # Return the toplevel that owns us
-
- def toplevel
- return @toplevel if defined? @toplevel
- @toplevel = self
- @toplevel = @toplevel.parent until TopLevel === @toplevel
- @toplevel
- end
-
- # allow us to sort modules by name
- def <=>(other)
- name <=> other.name
- end
-
- # Look up the given symbol. If method is non-nil, then
- # we assume the symbol references a module that
- # contains that method
- def find_symbol(symbol, method=nil)
- result = nil
- case symbol
- when /^::(.*)/
- result = toplevel.find_symbol($1)
- when /::/
- modules = symbol.split(/::/)
- unless modules.empty?
- module_name = modules.shift
- result = find_module_named(module_name)
- if result
- modules.each do |module_name|
- result = result.find_module_named(module_name)
- break unless result
- end
- end
- end
- else
- # if a method is specified, then we're definitely looking for
- # a module, otherwise it could be any symbol
- if method
- result = find_module_named(symbol)
- else
- result = find_local_symbol(symbol)
- if result.nil?
- if symbol =~ /^[A-Z]/
- result = parent
- while result && result.name != symbol
- result = result.parent
- end
- end
- end
- end
- end
- if result && method
- if !result.respond_to?(:find_local_symbol)
- p result.name
- p method
- fail
- end
- result = result.find_local_symbol(method)
- end
- result
- end
-
- def find_local_symbol(symbol)
- res = find_method_named(symbol) ||
- find_constant_named(symbol) ||
- find_attribute_named(symbol) ||
- find_module_named(symbol)
- end
-
- # Handle sections
-
- def set_current_section(title, comment)
- @current_section = Section.new(title, comment)
- @sections << @current_section
- end
-
- private
-
- # Find a named method, or return nil
- def find_method_named(name)
- @method_list.find {|meth| meth.name == name}
- end
-
- # Find a named instance method, or return nil
- def find_instance_method_named(name)
- @method_list.find {|meth| meth.name == name && !meth.singleton}
- end
-
- # Find a named constant, or return nil
- def find_constant_named(name)
- @constants.find {|m| m.name == name}
- end
-
- # Find a named attribute, or return nil
- def find_attribute_named(name)
- @attributes.find {|m| m.name == name}
- end
-
- end
-
-
- # A TopLevel context is a source file
-
- class TopLevel < Context
- attr_accessor :file_stat
- attr_accessor :file_relative_name
- attr_accessor :file_absolute_name
- attr_accessor :diagram
-
- @@all_classes = {}
- @@all_modules = {}
-
- def TopLevel::reset
- @@all_classes = {}
- @@all_modules = {}
- end
-
- def initialize(file_name)
- super()
- @name = "TopLevel"
- @file_relative_name = file_name
- @file_absolute_name = file_name
- @file_stat = File.stat(file_name)
- @diagram = nil
- end
-
- def full_name
- nil
- end
-
- # Adding a class or module to a TopLevel is special, as we only
- # want one copy of a particular top-level class. For example,
- # if both file A and file B implement class C, we only want one
- # ClassModule object for C. This code arranges to share
- # classes and modules between files.
-
- def add_class_or_module(collection, class_type, name, superclass)
- cls = collection[name]
- if cls
- puts "Reusing class/module #{name}" if $DEBUG
- else
- if class_type == NormalModule
- all = @@all_modules
- else
- all = @@all_classes
- end
- cls = all[name]
- if !cls
- cls = class_type.new(name, superclass)
- all[name] = cls unless @done_documenting
- end
- puts "Adding class/module #{name} to #@name" if $DEBUG
- collection[name] = cls unless @done_documenting
- cls.parent = self
- end
- cls
- end
-
- def TopLevel.all_classes_and_modules
- @@all_classes.values + @@all_modules.values
- end
-
- def TopLevel.find_class_named(name)
- @@all_classes.each_value do |c|
- res = c.find_class_named(name)
- return res if res
- end
- nil
- end
-
- def find_local_symbol(symbol)
- find_class_or_module_named(symbol) || super
- end
-
- def find_class_or_module_named(symbol)
- @@all_classes.each_value {|c| return c if c.name == symbol}
- @@all_modules.each_value {|m| return m if m.name == symbol}
- nil
- end
-
- # 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
- # class or a module.
-
- class ClassModule < Context
-
- attr_reader :superclass
- attr_accessor :diagram
-
- def initialize(name, superclass = nil)
- @name = name
- @diagram = nil
- @superclass = superclass
- @comment = ""
- super()
- end
-
- # Return the fully qualified name of this class or module
- def full_name
- if @parent && @parent.full_name
- @parent.full_name + "::" + @name
- else
- @name
- end
- end
-
- def http_url(prefix)
- path = full_name.split("::")
- File.join(prefix, *path) + ".html"
- end
-
- # Return +true+ if this object represents a module
- def is_module?
- false
- end
-
- # to_s is simply for debugging
- def to_s
- res = self.class.name + ": " + @name
- res << @comment.to_s
- res << super
- res
- end
-
- def find_class_named(name)
- return self if full_name == name
- @classes.each_value {|c| return c if c.find_class_named(name) }
- nil
- end
- end
-
- # Anonymous classes
- class AnonClass < ClassModule
- end
-
- # Normal classes
- class NormalClass < ClassModule
- end
-
- # Singleton classes
- class SingleClass < ClassModule
- end
-
- # Module
- class NormalModule < ClassModule
- def is_module?
- true
- end
- end
-
-
- # AnyMethod is the base class for objects representing methods
-
- class AnyMethod < CodeObject
- attr_accessor :name
- attr_accessor :visibility
- attr_accessor :block_params
- attr_accessor :dont_rename_initialize
- attr_accessor :singleton
- attr_reader :aliases # list of other names for this method
- attr_accessor :is_alias_for # or a method we're aliasing
-
- attr_overridable :params, :param, :parameters, :parameter
-
- attr_accessor :call_seq
-
-
- include TokenStream
-
- def initialize(text, name)
- super()
- @text = text
- @name = name
- @token_stream = nil
- @visibility = :public
- @dont_rename_initialize = false
- @block_params = nil
- @aliases = []
- @is_alias_for = nil
- @comment = ""
- @call_seq = nil
- end
-
- def <=>(other)
- @name <=> other.name
- end
-
- def to_s
- res = self.class.name + ": " + @name + " (" + @text + ")\n"
- res << @comment.to_s
- res
- end
-
- def param_seq
- p = params.gsub(/\s*\#.*/, '')
- p = p.tr("\n", " ").squeeze(" ")
- p = "(" + p + ")" unless p[0] == ?(
-
- if (block = block_params)
- # If this method has explicit block parameters, remove any
- # explicit &block
-$stderr.puts p
- p.sub!(/,?\s*&\w+/)
-$stderr.puts p
-
- block.gsub!(/\s*\#.*/, '')
- block = block.tr("\n", " ").squeeze(" ")
- if block[0] == ?(
- block.sub!(/^\(/, '').sub!(/\)/, '')
- end
- p << " {|#{block}| ...}"
- end
- p
- end
-
- def add_alias(method)
- @aliases << method
- end
- end
-
-
- # Represent an alias, which is an old_name/ new_name pair associated
- # with a particular context
- class Alias < CodeObject
- attr_accessor :text, :old_name, :new_name, :comment
-
- def initialize(text, old_name, new_name, comment)
- super()
- @text = text
- @old_name = old_name
- @new_name = new_name
- self.comment = comment
- end
-
- def to_s
- "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
- end
- end
-
- # Represent a constant
- class Constant < CodeObject
- attr_accessor :name, :value
-
- def initialize(name, value, comment)
- super()
- @name = name
- @value = value
- self.comment = comment
- end
- end
-
- # Represent attributes
- class Attr < CodeObject
- attr_accessor :text, :name, :rw, :visibility
-
- def initialize(text, name, rw, comment)
- super()
- @text = text
- @name = name
- @rw = rw
- @visibility = :public
- self.comment = comment
- end
-
- def to_s
- "attr: #{self.name} #{self.rw}\n#{self.comment}"
- end
-
- def <=>(other)
- self.name <=> other.name
- end
- end
-
- # a required file
-
- class Require < CodeObject
- attr_accessor :name
-
- def initialize(name, comment)
- super()
- @name = name.gsub(/'|"/, "") #'
- self.comment = comment
- end
-
- end
-
- # an included module
- class Include < CodeObject
- attr_accessor :name
-
- def initialize(name, comment)
- super()
- @name = name
- self.comment = comment
- end
-
- end
-
-end
diff --git a/lib/rdoc/comment.rb b/lib/rdoc/comment.rb
new file mode 100644
index 0000000000..134f6440a0
--- /dev/null
+++ b/lib/rdoc/comment.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+##
+# A comment holds the text comment for a RDoc::CodeObject and provides a
+# unified way of cleaning it up and parsing it into an RDoc::Markup::Document.
+#
+# Each comment may have a different markup format set by #format=. By default
+# 'rdoc' is used. The :markup: directive tells RDoc which format to use.
+#
+# See RDoc::Markup@Other+directives for instructions on adding an alternate
+# format.
+
+class RDoc::Comment
+
+ include RDoc::Text
+
+ ##
+ # The format of this comment. Defaults to RDoc::Markup
+
+ attr_reader :format
+
+ ##
+ # The RDoc::TopLevel this comment was found in
+
+ attr_accessor :location
+
+ ##
+ # For duck-typing when merging classes at load time
+
+ alias file location # :nodoc:
+
+ ##
+ # The text for this comment
+
+ attr_reader :text
+
+ ##
+ # Overrides the content returned by #parse. Use when there is no #text
+ # source for this comment
+
+ attr_writer :document
+
+ ##
+ # Creates a new comment with +text+ that is found in the RDoc::TopLevel
+ # +location+.
+
+ def initialize text = nil, location = nil
+ @location = location
+ @text = text.nil? ? nil : text.dup
+
+ @document = nil
+ @format = 'rdoc'
+ @normalized = false
+ end
+
+ ##
+ #--
+ # TODO deep copy @document
+
+ def initialize_copy copy # :nodoc:
+ @text = copy.text.dup
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ other.text == @text and other.location == @location
+ end
+
+ ##
+ # Look for a 'call-seq' in the comment to override the normal parameter
+ # handling. The :call-seq: is indented from the baseline. All lines of the
+ # same indentation level and prefix are consumed.
+ #
+ # For example, all of the following will be used as the :call-seq:
+ #
+ # # :call-seq:
+ # # ARGF.readlines(sep=$/) -> array
+ # # ARGF.readlines(limit) -> array
+ # # ARGF.readlines(sep, limit) -> array
+ # #
+ # # ARGF.to_a(sep=$/) -> array
+ # # ARGF.to_a(limit) -> array
+ # # ARGF.to_a(sep, limit) -> array
+
+ def extract_call_seq method
+ # we must handle situations like the above followed by an unindented first
+ # comment. The difficulty is to make sure not to match lines starting
+ # with ARGF at the same indent, but that are after the first description
+ # paragraph.
+ if @text =~ /^\s*:?call-seq:(.*?(?:\S).*?)^\s*$/m then
+ all_start, all_stop = $~.offset(0)
+ seq_start, seq_stop = $~.offset(1)
+
+ # we get the following lines that start with the leading word at the
+ # same indent, even if they have blank lines before
+ if $1 =~ /(^\s*\n)+^(\s*\w+)/m then
+ leading = $2 # ' * ARGF' in the example above
+ re = %r%
+ \A(
+ (^\s*\n)+
+ (^#{Regexp.escape leading}.*?\n)+
+ )+
+ ^\s*$
+ %xm
+
+ if @text[seq_stop..-1] =~ re then
+ all_stop = seq_stop + $~.offset(0).last
+ seq_stop = seq_stop + $~.offset(1).last
+ end
+ end
+
+ seq = @text[seq_start..seq_stop]
+ seq.gsub!(/^\s*(\S|\n)/m, '\1')
+ @text.slice! all_start...all_stop
+
+ method.call_seq = seq.chomp
+
+ else
+ regexp = /^\s*:?call-seq:(.*?)(^\s*$|\z)/m
+ if regexp =~ @text then
+ @text = @text.sub(regexp, '')
+ seq = $1
+ seq.gsub!(/^\s*/, '')
+ method.call_seq = seq
+ end
+ end
+
+ method
+ end
+
+ ##
+ # A comment is empty if its text String is empty.
+
+ def empty?
+ @text.empty?
+ end
+
+ ##
+ # HACK dubious
+
+ def encode! encoding
+ # TODO: Remove this condition after Ruby 2.2 EOL
+ if RUBY_VERSION < '2.3.0'
+ @text = @text.force_encoding encoding
+ else
+ @text = String.new @text, encoding: encoding
+ end
+ self
+ end
+
+ ##
+ # Sets the format of this comment and resets any parsed document
+
+ def format= format
+ @format = format
+ @document = nil
+ end
+
+ def inspect # :nodoc:
+ location = @location ? @location.relative_name : '(unknown)'
+
+ "#<%s:%x %s %p>" % [self.class, object_id, location, @text]
+ end
+
+ ##
+ # Normalizes the text. See RDoc::Text#normalize_comment for details
+
+ def normalize
+ return self unless @text
+ return self if @normalized # TODO eliminate duplicate normalization
+
+ @text = normalize_comment @text
+
+ @normalized = true
+
+ self
+ end
+
+ ##
+ # Was this text normalized?
+
+ def normalized? # :nodoc:
+ @normalized
+ end
+
+ ##
+ # Parses the comment into an RDoc::Markup::Document. The parsed document is
+ # cached until the text is changed.
+
+ def parse
+ return @document if @document
+
+ @document = super @text, @format
+ @document.file = @location
+ @document
+ end
+
+ ##
+ # Removes private sections from this comment. Private sections are flush to
+ # the comment marker and start with <tt>--</tt> and end with <tt>++</tt>.
+ # For C-style comments, a private marker may not start at the opening of the
+ # comment.
+ #
+ # /*
+ # *--
+ # * private
+ # *++
+ # * public
+ # */
+
+ def remove_private
+ # Workaround for gsub encoding for Ruby 1.9.2 and earlier
+ empty = ''
+ empty = RDoc::Encoding.change_encoding empty, @text.encoding
+
+ @text = @text.gsub(%r%^\s*([#*]?)--.*?^\s*(\1)\+\+\n?%m, empty)
+ @text = @text.sub(%r%^\s*[#*]?--.*%m, '')
+ end
+
+ ##
+ # Replaces this comment's text with +text+ and resets the parsed document.
+ #
+ # An error is raised if the comment contains a document but no text.
+
+ def text= text
+ raise RDoc::Error, 'replacing document-only comment is not allowed' if
+ @text.nil? and @document
+
+ @document = nil
+ @text = text.nil? ? nil : text.dup
+ end
+
+ ##
+ # Returns true if this comment is in TomDoc format.
+
+ def tomdoc?
+ @format == 'tomdoc'
+ end
+
+end
diff --git a/lib/rdoc/constant.rb b/lib/rdoc/constant.rb
new file mode 100644
index 0000000000..0c3d7505a1
--- /dev/null
+++ b/lib/rdoc/constant.rb
@@ -0,0 +1,187 @@
+# frozen_string_literal: true
+##
+# A constant
+
+class RDoc::Constant < RDoc::CodeObject
+
+ MARSHAL_VERSION = 0 # :nodoc:
+
+ ##
+ # Sets the module or class this is constant is an alias for.
+
+ attr_writer :is_alias_for
+
+ ##
+ # The constant's name
+
+ attr_accessor :name
+
+ ##
+ # The constant's value
+
+ attr_accessor :value
+
+ ##
+ # The constant's visibility
+
+ attr_accessor :visibility
+
+ ##
+ # Creates a new constant with +name+, +value+ and +comment+
+
+ def initialize(name, value, comment)
+ super()
+
+ @name = name
+ @value = value
+
+ @is_alias_for = nil
+ @visibility = :public
+
+ self.comment = comment
+ end
+
+ ##
+ # Constants are ordered by name
+
+ def <=> other
+ return unless self.class === other
+
+ [parent_name, name] <=> [other.parent_name, other.name]
+ end
+
+ ##
+ # Constants are equal when their #parent and #name is the same
+
+ def == other
+ self.class == other.class and
+ @parent == other.parent and
+ @name == other.name
+ end
+
+ ##
+ # A constant is documented if it has a comment, or is an alias
+ # for a documented class or module.
+
+ def documented?
+ return true if super
+ return false unless @is_alias_for
+ case @is_alias_for
+ when String then
+ found = @store.find_class_or_module @is_alias_for
+ return false unless found
+ @is_alias_for = found
+ end
+ @is_alias_for.documented?
+ end
+
+ ##
+ # Full constant name including namespace
+
+ def full_name
+ @full_name ||= "#{parent_name}::#{@name}"
+ end
+
+ ##
+ # The module or class this constant is an alias for
+
+ def is_alias_for
+ case @is_alias_for
+ when String then
+ found = @store.find_class_or_module @is_alias_for
+ @is_alias_for = found if found
+ @is_alias_for
+ else
+ @is_alias_for
+ end
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %s::%s>" % [
+ self.class, object_id,
+ parent_name, @name,
+ ]
+ end
+
+ ##
+ # Dumps this Constant for use by ri. See also #marshal_load
+
+ def marshal_dump
+ alias_name = case found = is_alias_for
+ when RDoc::CodeObject then found.full_name
+ else found
+ end
+
+ [ MARSHAL_VERSION,
+ @name,
+ full_name,
+ @visibility,
+ alias_name,
+ parse(@comment),
+ @file.relative_name,
+ parent.name,
+ parent.class,
+ section.title,
+ ]
+ end
+
+ ##
+ # Loads this Constant from +array+. For a loaded Constant the following
+ # methods will return cached values:
+ #
+ # * #full_name
+ # * #parent_name
+
+ def marshal_load array
+ initialize array[1], nil, array[5]
+
+ @full_name = array[2]
+ @visibility = array[3] || :public
+ @is_alias_for = array[4]
+ # 5 handled above
+ # 6 handled below
+ @parent_name = array[7]
+ @parent_class = array[8]
+ @section_title = array[9]
+
+ @file = RDoc::TopLevel.new array[6]
+ end
+
+ ##
+ # Path to this constant for use with HTML generator output.
+
+ def path
+ "#{@parent.path}##{@name}"
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[#{self.class.name} #{full_name}", "]" do
+ unless comment.empty? then
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
+ end
+ end
+
+ ##
+ # Sets the store for this class or module and its contained code objects.
+
+ def store= store
+ super
+
+ @file = @store.add_file @file.full_name if @file
+ end
+
+ def to_s # :nodoc:
+ parent_name = parent ? parent.full_name : '(unknown)'
+ if is_alias_for
+ "constant #{parent_name}::#@name -> #{is_alias_for}"
+ else
+ "constant #{parent_name}::#@name"
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb
new file mode 100644
index 0000000000..58b1c54269
--- /dev/null
+++ b/lib/rdoc/context.rb
@@ -0,0 +1,1235 @@
+# frozen_string_literal: true
+require 'cgi'
+
+##
+# A Context is something that can hold modules, classes, methods, attributes,
+# aliases, requires, and includes. Classes, modules, and files are all
+# Contexts.
+
+class RDoc::Context < RDoc::CodeObject
+
+ include Comparable
+
+ ##
+ # Types of methods
+
+ TYPES = %w[class instance]
+
+ ##
+ # If a context has these titles it will be sorted in this order.
+
+ TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
+ TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
+
+ ##
+ # Class/module aliases
+
+ attr_reader :aliases
+
+ ##
+ # All attr* methods
+
+ attr_reader :attributes
+
+ ##
+ # Block params to be used in the next MethodAttr parsed under this context
+
+ attr_accessor :block_params
+
+ ##
+ # Constants defined
+
+ attr_reader :constants
+
+ ##
+ # Sets the current documentation section of documentation
+
+ attr_writer :current_section
+
+ ##
+ # Files this context is found in
+
+ attr_reader :in_files
+
+ ##
+ # Modules this context includes
+
+ attr_reader :includes
+
+ ##
+ # Modules this context is extended with
+
+ attr_reader :extends
+
+ ##
+ # Methods defined in this context
+
+ attr_reader :method_list
+
+ ##
+ # Name of this class excluding namespace. See also full_name
+
+ attr_reader :name
+
+ ##
+ # Files this context requires
+
+ attr_reader :requires
+
+ ##
+ # Use this section for the next method, attribute or constant added.
+
+ attr_accessor :temporary_section
+
+ ##
+ # Hash <tt>old_name => [aliases]</tt>, for aliases
+ # that haven't (yet) been resolved to a method/attribute.
+ # (Not to be confused with the aliases of the context.)
+
+ attr_accessor :unmatched_alias_lists
+
+ ##
+ # Aliases that could not be resolved.
+
+ attr_reader :external_aliases
+
+ ##
+ # Current visibility of this context
+
+ attr_accessor :visibility
+
+ ##
+ # Current visibility of this line
+
+ attr_writer :current_line_visibility
+
+ ##
+ # Hash of registered methods. Attributes are also registered here,
+ # twice if they are RW.
+
+ attr_reader :methods_hash
+
+ ##
+ # Params to be used in the next MethodAttr parsed under this context
+
+ attr_accessor :params
+
+ ##
+ # Hash of registered constants.
+
+ attr_reader :constants_hash
+
+ ##
+ # Creates an unnamed empty context with public current visibility
+
+ def initialize
+ super
+
+ @in_files = []
+
+ @name ||= "unknown"
+ @parent = nil
+ @visibility = :public
+
+ @current_section = Section.new self, nil, nil
+ @sections = { nil => @current_section }
+ @temporary_section = nil
+
+ @classes = {}
+ @modules = {}
+
+ initialize_methods_etc
+ end
+
+ ##
+ # Sets the defaults for methods and so-forth
+
+ def initialize_methods_etc
+ @method_list = []
+ @attributes = []
+ @aliases = []
+ @requires = []
+ @includes = []
+ @extends = []
+ @constants = []
+ @external_aliases = []
+ @current_line_visibility = nil
+
+ # This Hash maps a method name to a list of unmatched aliases (aliases of
+ # a method not yet encountered).
+ @unmatched_alias_lists = {}
+
+ @methods_hash = {}
+ @constants_hash = {}
+
+ @params = nil
+
+ @store ||= nil
+ end
+
+ ##
+ # Contexts are sorted by full_name
+
+ def <=>(other)
+ return nil unless RDoc::CodeObject === other
+
+ full_name <=> other.full_name
+ end
+
+ ##
+ # Adds an item of type +klass+ with the given +name+ and +comment+ to the
+ # context.
+ #
+ # Currently only RDoc::Extend and RDoc::Include are supported.
+
+ def add klass, name, comment
+ if RDoc::Extend == klass then
+ ext = RDoc::Extend.new name, comment
+ add_extend ext
+ elsif RDoc::Include == klass then
+ incl = RDoc::Include.new name, comment
+ add_include incl
+ else
+ raise NotImplementedError, "adding a #{klass} is not implemented"
+ end
+ end
+
+ ##
+ # Adds +an_alias+ that is automatically resolved
+
+ def add_alias an_alias
+ return an_alias unless @document_self
+
+ method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
+ find_attribute(an_alias.old_name, an_alias.singleton)
+
+ if method_attr then
+ method_attr.add_alias an_alias, self
+ else
+ add_to @external_aliases, an_alias
+ unmatched_alias_list =
+ @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
+ unmatched_alias_list.push an_alias
+ end
+
+ an_alias
+ end
+
+ ##
+ # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
+ # updates the comment if it was empty.
+ #
+ # The attribute is registered only if it defines a new method.
+ # For instance, <tt>attr_reader :foo</tt> will not be registered
+ # if method +foo+ exists, but <tt>attr_accessor :foo</tt> will be registered
+ # if method +foo+ exists, but <tt>foo=</tt> does not.
+
+ def add_attribute attribute
+ return attribute unless @document_self
+
+ # mainly to check for redefinition of an attribute as a method
+ # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
+ register = false
+
+ key = nil
+
+ if attribute.rw.index 'R' then
+ key = attribute.pretty_name
+ known = @methods_hash[key]
+
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name + '='] and
+ RDoc::Attr === registered then
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
+
+ if attribute.rw.index 'W' then
+ key = attribute.pretty_name + '='
+ known = @methods_hash[key]
+
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name] and
+ RDoc::Attr === registered then
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
+
+ if register then
+ attribute.visibility = @visibility
+ add_to @attributes, attribute
+ resolve_aliases attribute
+ end
+
+ attribute
+ end
+
+ ##
+ # Adds a class named +given_name+ with +superclass+.
+ #
+ # Both +given_name+ and +superclass+ may contain '::', and are
+ # interpreted relative to the +self+ context. This allows handling correctly
+ # examples like these:
+ # class RDoc::Gauntlet < Gauntlet
+ # module Mod
+ # class Object # implies < ::Object
+ # class SubObject < Object # this is _not_ ::Object
+ #
+ # Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module
+ # unless it later sees <tt>class Container</tt>. +add_class+ automatically
+ # upgrades +given_name+ to a class in this case.
+
+ def add_class class_type, given_name, superclass = '::Object'
+ # superclass +nil+ is passed by the C parser in the following cases:
+ # - registering Object in 1.8 (correct)
+ # - registering BasicObject in 1.9 (correct)
+ # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
+ #
+ # If we later find a superclass for a registered class with a nil
+ # superclass, we must honor it.
+
+ # find the name & enclosing context
+ if given_name =~ /^:+(\w+)$/ then
+ full_name = $1
+ enclosing = top_level
+ name = full_name.split(/:+/).last
+ else
+ full_name = child_name given_name
+
+ if full_name =~ /^(.+)::(\w+)$/ then
+ name = $2
+ ename = $1
+ enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
+ # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
+ unless enclosing then
+ # try the given name at top level (will work for the above example)
+ enclosing = @store.classes_hash[given_name] ||
+ @store.modules_hash[given_name]
+ return enclosing if enclosing
+ # not found: create the parent(s)
+ names = ename.split('::')
+ enclosing = self
+ names.each do |n|
+ enclosing = enclosing.classes_hash[n] ||
+ enclosing.modules_hash[n] ||
+ enclosing.add_module(RDoc::NormalModule, n)
+ end
+ end
+ else
+ name = full_name
+ enclosing = self
+ end
+ end
+
+ # fix up superclass
+ if full_name == 'BasicObject' then
+ superclass = nil
+ elsif full_name == 'Object' then
+ superclass = '::BasicObject'
+ end
+
+ # find the superclass full name
+ if superclass then
+ if superclass =~ /^:+/ then
+ superclass = $' #'
+ else
+ if superclass =~ /^(\w+):+(.+)$/ then
+ suffix = $2
+ mod = find_module_named($1)
+ superclass = mod.full_name + '::' + suffix if mod
+ else
+ mod = find_module_named(superclass)
+ superclass = mod.full_name if mod
+ end
+ end
+
+ # did we believe it was a module?
+ mod = @store.modules_hash.delete superclass
+
+ upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
+
+ # e.g., Object < Object
+ superclass = nil if superclass == full_name
+ end
+
+ klass = @store.classes_hash[full_name]
+
+ if klass then
+ # if TopLevel, it may not be registered in the classes:
+ enclosing.classes_hash[name] = klass
+
+ # update the superclass if needed
+ if superclass then
+ existing = klass.superclass
+ existing = existing.full_name unless existing.is_a?(String) if existing
+ if existing.nil? ||
+ (existing == 'Object' && superclass != 'Object') then
+ klass.superclass = superclass
+ end
+ end
+ else
+ # this is a new class
+ mod = @store.modules_hash.delete full_name
+
+ if mod then
+ klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
+
+ klass.superclass = superclass unless superclass.nil?
+ else
+ klass = class_type.new name, superclass
+
+ enclosing.add_class_or_module(klass, enclosing.classes_hash,
+ @store.classes_hash)
+ end
+ end
+
+ klass.parent = self
+
+ klass
+ end
+
+ ##
+ # Adds the class or module +mod+ to the modules or
+ # classes Hash +self_hash+, and to +all_hash+ (either
+ # <tt>TopLevel::modules_hash</tt> or <tt>TopLevel::classes_hash</tt>),
+ # unless #done_documenting is +true+. Sets the #parent of +mod+
+ # to +self+, and its #section to #current_section. Returns +mod+.
+
+ def add_class_or_module mod, self_hash, all_hash
+ mod.section = current_section # TODO declaring context? something is
+ # wrong here...
+ mod.parent = self
+ mod.store = @store
+
+ unless @done_documenting then
+ self_hash[mod.name] = mod
+ # this must be done AFTER adding mod to its parent, so that the full
+ # name is correct:
+ all_hash[mod.full_name] = mod
+ end
+
+ mod
+ end
+
+ ##
+ # Adds +constant+ if not already there. If it is, updates the comment,
+ # value and/or is_alias_for of the known constant if they were empty/nil.
+
+ def add_constant constant
+ return constant unless @document_self
+
+ # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
+ # (this is a #ifdef: should be handled by the C parser)
+ known = @constants_hash[constant.name]
+
+ if known then
+ known.comment = constant.comment if known.comment.empty?
+
+ known.value = constant.value if
+ known.value.nil? or known.value.strip.empty?
+
+ known.is_alias_for ||= constant.is_alias_for
+ else
+ @constants_hash[constant.name] = constant
+ add_to @constants, constant
+ end
+
+ constant
+ end
+
+ ##
+ # Adds included module +include+ which should be an RDoc::Include
+
+ def add_include include
+ add_to @includes, include
+
+ include
+ end
+
+ ##
+ # Adds extension module +ext+ which should be an RDoc::Extend
+
+ def add_extend ext
+ add_to @extends, ext
+
+ ext
+ end
+
+ ##
+ # Adds +method+ if not already there. If it is (as method or attribute),
+ # updates the comment if it was empty.
+
+ def add_method method
+ return method unless @document_self
+
+ # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
+ key = method.pretty_name
+ known = @methods_hash[key]
+
+ if known then
+ if @store then # otherwise we are loading
+ known.comment = method.comment if known.comment.empty?
+ previously = ", previously in #{known.file}" unless
+ method.file == known.file
+ @store.rdoc.options.warn \
+ "Duplicate method #{known.full_name} in #{method.file}#{previously}"
+ end
+ else
+ @methods_hash[key] = method
+ if @current_line_visibility
+ method.visibility, @current_line_visibility = @current_line_visibility, nil
+ else
+ method.visibility = @visibility
+ end
+ add_to @method_list, method
+ resolve_aliases method
+ end
+
+ method
+ end
+
+ ##
+ # Adds a module named +name+. If RDoc already knows +name+ is a class then
+ # that class is returned instead. See also #add_class.
+
+ def add_module(class_type, name)
+ mod = @classes[name] || @modules[name]
+ return mod if mod
+
+ full_name = child_name name
+ mod = @store.modules_hash[full_name] || class_type.new(name)
+
+ add_class_or_module mod, @modules, @store.modules_hash
+ end
+
+ ##
+ # Adds an alias from +from+ (a class or module) to +name+ which was defined
+ # in +file+.
+
+ def add_module_alias from, name, file
+ return from if @done_documenting
+
+ to_name = child_name name
+
+ # if we already know this name, don't register an alias:
+ # see the metaprogramming in lib/active_support/basic_object.rb,
+ # where we already know BasicObject is a class when we find
+ # BasicObject = BlankSlate
+ return from if @store.find_class_or_module to_name
+
+ to = from.dup
+ to.name = name
+ to.full_name = nil
+
+ if to.module? then
+ @store.modules_hash[to_name] = to
+ @modules[name] = to
+ else
+ @store.classes_hash[to_name] = to
+ @classes[name] = to
+ end
+
+ # Registers a constant for this alias. The constant value and comment
+ # will be updated later, when the Ruby parser adds the constant
+ const = RDoc::Constant.new name, nil, to.comment
+ const.record_location file
+ const.is_alias_for = from
+ add_constant const
+
+ to
+ end
+
+ ##
+ # Adds +require+ to this context's top level
+
+ def add_require(require)
+ return require unless @document_self
+
+ if RDoc::TopLevel === self then
+ add_to @requires, require
+ else
+ parent.add_require require
+ end
+ end
+
+ ##
+ # Returns a section with +title+, creating it if it doesn't already exist.
+ # +comment+ will be appended to the section's comment.
+ #
+ # A section with a +title+ of +nil+ will return the default section.
+ #
+ # See also RDoc::Context::Section
+
+ def add_section title, comment = nil
+ if section = @sections[title] then
+ section.add_comment comment if comment
+ else
+ section = Section.new self, title, comment
+ @sections[title] = section
+ end
+
+ section
+ end
+
+ ##
+ # Adds +thing+ to the collection +array+
+
+ def add_to array, thing
+ array << thing if @document_self
+
+ thing.parent = self
+ thing.store = @store if @store
+ thing.section = current_section
+ end
+
+ ##
+ # Is there any content?
+ #
+ # This means any of: comment, aliases, methods, attributes, external
+ # aliases, require, constant.
+ #
+ # Includes and extends are also checked unless <tt>includes == false</tt>.
+
+ def any_content(includes = true)
+ @any_content ||= !(
+ @comment.empty? &&
+ @method_list.empty? &&
+ @attributes.empty? &&
+ @aliases.empty? &&
+ @external_aliases.empty? &&
+ @requires.empty? &&
+ @constants.empty?
+ )
+ @any_content || (includes && !(@includes + @extends).empty? )
+ end
+
+ ##
+ # Creates the full name for a child with +name+
+
+ def child_name name
+ if name =~ /^:+/
+ $' #'
+ elsif RDoc::TopLevel === self then
+ name
+ else
+ "#{self.full_name}::#{name}"
+ end
+ end
+
+ ##
+ # Class attributes
+
+ def class_attributes
+ @class_attributes ||= attributes.select { |a| a.singleton }
+ end
+
+ ##
+ # Class methods
+
+ def class_method_list
+ @class_method_list ||= method_list.select { |a| a.singleton }
+ end
+
+ ##
+ # Array of classes in this context
+
+ def classes
+ @classes.values
+ end
+
+ ##
+ # All classes and modules in this namespace
+
+ def classes_and_modules
+ classes + modules
+ end
+
+ ##
+ # Hash of classes keyed by class name
+
+ def classes_hash
+ @classes
+ end
+
+ ##
+ # The current documentation section that new items will be added to. If
+ # temporary_section is available it will be used.
+
+ def current_section
+ if section = @temporary_section then
+ @temporary_section = nil
+ else
+ section = @current_section
+ end
+
+ section
+ end
+
+ ##
+ # Is part of this thing was defined in +file+?
+
+ def defined_in?(file)
+ @in_files.include?(file)
+ end
+
+ def display(method_attr) # :nodoc:
+ if method_attr.is_a? RDoc::Attr
+ "#{method_attr.definition} #{method_attr.pretty_name}"
+ else
+ "method #{method_attr.pretty_name}"
+ end
+ end
+
+ ##
+ # Iterator for ancestors for duck-typing. Does nothing. See
+ # RDoc::ClassModule#each_ancestor.
+ #
+ # This method exists to make it easy to work with Context subclasses that
+ # aren't part of RDoc.
+
+ def each_ancestor # :nodoc:
+ end
+
+ ##
+ # Iterator for attributes
+
+ def each_attribute # :yields: attribute
+ @attributes.each { |a| yield a }
+ end
+
+ ##
+ # Iterator for classes and modules
+
+ def each_classmodule(&block) # :yields: module
+ classes_and_modules.sort.each(&block)
+ end
+
+ ##
+ # Iterator for constants
+
+ def each_constant # :yields: constant
+ @constants.each {|c| yield c}
+ end
+
+ ##
+ # Iterator for included modules
+
+ def each_include # :yields: include
+ @includes.each do |i| yield i end
+ end
+
+ ##
+ # Iterator for extension modules
+
+ def each_extend # :yields: extend
+ @extends.each do |e| yield e end
+ end
+
+ ##
+ # Iterator for methods
+
+ def each_method # :yields: method
+ return enum_for __method__ unless block_given?
+
+ @method_list.sort.each { |m| yield m }
+ end
+
+ ##
+ # Iterator for each section's contents sorted by title. The +section+, the
+ # section's +constants+ and the sections +attributes+ are yielded. The
+ # +constants+ and +attributes+ collections are sorted.
+ #
+ # To retrieve methods in a section use #methods_by_type with the optional
+ # +section+ parameter.
+ #
+ # NOTE: Do not edit collections yielded by this method
+
+ def each_section # :yields: section, constants, attributes
+ return enum_for __method__ unless block_given?
+
+ constants = @constants.group_by do |constant| constant.section end
+ attributes = @attributes.group_by do |attribute| attribute.section end
+
+ constants.default = []
+ attributes.default = []
+
+ sort_sections.each do |section|
+ yield section, constants[section].select(&:display?).sort, attributes[section].select(&:display?).sort
+ end
+ end
+
+ ##
+ # Finds an attribute +name+ with singleton value +singleton+.
+
+ def find_attribute(name, singleton)
+ name = $1 if name =~ /^(.*)=$/
+ @attributes.find { |a| a.name == name && a.singleton == singleton }
+ end
+
+ ##
+ # Finds an attribute with +name+ in this context
+
+ def find_attribute_named(name)
+ case name
+ when /\A#/ then
+ find_attribute name[1..-1], false
+ when /\A::/ then
+ find_attribute name[2..-1], true
+ else
+ @attributes.find { |a| a.name == name }
+ end
+ end
+
+ ##
+ # Finds a class method with +name+ in this context
+
+ def find_class_method_named(name)
+ @method_list.find { |meth| meth.singleton && meth.name == name }
+ end
+
+ ##
+ # Finds a constant with +name+ in this context
+
+ def find_constant_named(name)
+ @constants.find do |m|
+ m.name == name || m.full_name == name
+ end
+ end
+
+ ##
+ # Find a module at a higher scope
+
+ def find_enclosing_module_named(name)
+ parent && parent.find_module_named(name)
+ end
+
+ ##
+ # Finds an external alias +name+ with singleton value +singleton+.
+
+ def find_external_alias(name, singleton)
+ @external_aliases.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
+ # Finds an external alias with +name+ in this context
+
+ def find_external_alias_named(name)
+ case name
+ when /\A#/ then
+ find_external_alias name[1..-1], false
+ when /\A::/ then
+ find_external_alias name[2..-1], true
+ else
+ @external_aliases.find { |a| a.name == name }
+ end
+ end
+
+ ##
+ # Finds a file with +name+ in this context
+
+ def find_file_named name
+ @store.find_file_named name
+ end
+
+ ##
+ # Finds an instance method with +name+ in this context
+
+ def find_instance_method_named(name)
+ @method_list.find { |meth| !meth.singleton && meth.name == name }
+ end
+
+ ##
+ # Finds a method, constant, attribute, external alias, module or file
+ # named +symbol+ in this context.
+
+ def find_local_symbol(symbol)
+ find_method_named(symbol) or
+ find_constant_named(symbol) or
+ find_attribute_named(symbol) or
+ find_external_alias_named(symbol) or
+ find_module_named(symbol) or
+ find_file_named(symbol)
+ end
+
+ ##
+ # Finds a method named +name+ with singleton value +singleton+.
+
+ def find_method(name, singleton)
+ @method_list.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
+ # Finds a instance or module method with +name+ in this context
+
+ def find_method_named(name)
+ case name
+ when /\A#/ then
+ find_method name[1..-1], false
+ when /\A::/ then
+ find_method name[2..-1], true
+ else
+ @method_list.find { |meth| meth.name == name }
+ end
+ end
+
+ ##
+ # Find a module with +name+ using ruby's scoping rules
+
+ def find_module_named(name)
+ res = @modules[name] || @classes[name]
+ return res if res
+ return self if self.name == name
+ find_enclosing_module_named name
+ end
+
+ ##
+ # Look up +symbol+, first as a module, then as a local symbol.
+
+ def find_symbol(symbol)
+ find_symbol_module(symbol) || find_local_symbol(symbol)
+ end
+
+ ##
+ # Look up a module named +symbol+.
+
+ def find_symbol_module(symbol)
+ result = nil
+
+ # look for a class or module 'symbol'
+ case symbol
+ when /^::/ then
+ result = @store.find_class_or_module symbol
+ when /^(\w+):+(.+)$/
+ suffix = $2
+ top = $1
+ searched = self
+ while searched do
+ mod = searched.find_module_named(top)
+ break unless mod
+ result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
+ end
+ else
+ searched = self
+ while searched do
+ result = searched.find_module_named(symbol)
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
+ end
+ end
+
+ result
+ end
+
+ ##
+ # The full name for this context. This method is overridden by subclasses.
+
+ def full_name
+ '(unknown)'
+ end
+
+ ##
+ # Does this context and its methods and constants all have documentation?
+ #
+ # (Yes, fully documented doesn't mean everything.)
+
+ def fully_documented?
+ documented? and
+ attributes.all? { |a| a.documented? } and
+ method_list.all? { |m| m.documented? } and
+ constants.all? { |c| c.documented? }
+ end
+
+ ##
+ # URL for this with a +prefix+
+
+ def http_url(prefix)
+ path = name_for_path
+ path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</
+ path = [prefix] + path.split('::')
+
+ File.join(*path.compact) + '.html'
+ end
+
+ ##
+ # Instance attributes
+
+ def instance_attributes
+ @instance_attributes ||= attributes.reject { |a| a.singleton }
+ end
+
+ ##
+ # Instance methods
+ #--
+ # TODO rename to instance_methods
+
+ def instance_method_list
+ @instance_method_list ||= method_list.reject { |a| a.singleton }
+ end
+
+ ##
+ # Breaks method_list into a nested hash by type (<tt>'class'</tt> or
+ # <tt>'instance'</tt>) and visibility (+:public+, +:protected+, +:private+).
+ #
+ # If +section+ is provided only methods in that RDoc::Context::Section will
+ # be returned.
+
+ def methods_by_type section = nil
+ methods = {}
+
+ TYPES.each do |type|
+ visibilities = {}
+ RDoc::VISIBILITIES.each do |vis|
+ visibilities[vis] = []
+ end
+
+ methods[type] = visibilities
+ end
+
+ each_method do |method|
+ next if section and not method.section == section
+ methods[method.type][method.visibility] << method
+ end
+
+ methods
+ end
+
+ ##
+ # Yields AnyMethod and Attr entries matching the list of names in +methods+.
+
+ def methods_matching(methods, singleton = false, &block)
+ (@method_list + @attributes).each do |m|
+ yield m if methods.include?(m.name) and m.singleton == singleton
+ end
+
+ each_ancestor do |parent|
+ parent.methods_matching(methods, singleton, &block)
+ end
+ end
+
+ ##
+ # Array of modules in this context
+
+ def modules
+ @modules.values
+ end
+
+ ##
+ # Hash of modules keyed by module name
+
+ def modules_hash
+ @modules
+ end
+
+ ##
+ # Name to use to generate the url.
+ # <tt>#full_name</tt> by default.
+
+ def name_for_path
+ full_name
+ end
+
+ ##
+ # Changes the visibility for new methods to +visibility+
+
+ def ongoing_visibility=(visibility)
+ @visibility = visibility
+ end
+
+ ##
+ # Record +top_level+ as a file +self+ is in.
+
+ def record_location(top_level)
+ @in_files << top_level unless @in_files.include?(top_level)
+ end
+
+ ##
+ # Should we remove this context from the documentation?
+ #
+ # The answer is yes if:
+ # * #received_nodoc is +true+
+ # * #any_content is +false+ (not counting includes)
+ # * All #includes are modules (not a string), and their module has
+ # <tt>#remove_from_documentation? == true</tt>
+ # * All classes and modules have <tt>#remove_from_documentation? == true</tt>
+
+ def remove_from_documentation?
+ @remove_from_documentation ||=
+ @received_nodoc &&
+ !any_content(false) &&
+ @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
+ classes_and_modules.all? { |cm| cm.remove_from_documentation? }
+ end
+
+ ##
+ # Removes methods and attributes with a visibility less than +min_visibility+.
+ #--
+ # TODO mark the visibility of attributes in the template (if not public?)
+
+ def remove_invisible min_visibility
+ return if [:private, :nodoc].include? min_visibility
+ remove_invisible_in @method_list, min_visibility
+ remove_invisible_in @attributes, min_visibility
+ remove_invisible_in @constants, min_visibility
+ end
+
+ ##
+ # Only called when min_visibility == :public or :private
+
+ def remove_invisible_in array, min_visibility # :nodoc:
+ if min_visibility == :public then
+ array.reject! { |e|
+ e.visibility != :public and not e.force_documentation
+ }
+ else
+ array.reject! { |e|
+ e.visibility == :private and not e.force_documentation
+ }
+ end
+ end
+
+ ##
+ # Tries to resolve unmatched aliases when a method or attribute has just
+ # been added.
+
+ def resolve_aliases added
+ # resolve any pending unmatched aliases
+ key = added.pretty_name
+ unmatched_alias_list = @unmatched_alias_lists[key]
+ return unless unmatched_alias_list
+ unmatched_alias_list.each do |unmatched_alias|
+ added.add_alias unmatched_alias, self
+ @external_aliases.delete unmatched_alias
+ end
+ @unmatched_alias_lists.delete key
+ end
+
+ ##
+ # Returns RDoc::Context::Section objects referenced in this context for use
+ # in a table of contents.
+
+ def section_contents
+ used_sections = {}
+
+ each_method do |method|
+ next unless method.display?
+
+ used_sections[method.section] = true
+ end
+
+ # order found sections
+ sections = sort_sections.select do |section|
+ used_sections[section]
+ end
+
+ # only the default section is used
+ return [] if
+ sections.length == 1 and not sections.first.title
+
+ sections
+ end
+
+ ##
+ # Sections in this context
+
+ def sections
+ @sections.values
+ end
+
+ def sections_hash # :nodoc:
+ @sections
+ end
+
+ ##
+ # Sets the current section to a section with +title+. See also #add_section
+
+ def set_current_section title, comment
+ @current_section = add_section title, comment
+ end
+
+ ##
+ # Given an array +methods+ of method names, set the visibility of each to
+ # +visibility+
+
+ def set_visibility_for(methods, visibility, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = visibility
+ end
+ end
+
+ ##
+ # Given an array +names+ of constants, set the visibility of each constant to
+ # +visibility+
+
+ def set_constant_visibility_for(names, visibility)
+ names.each do |name|
+ constant = @constants_hash[name] or next
+ constant.visibility = visibility
+ end
+ end
+
+ ##
+ # Sorts sections alphabetically (default) or in TomDoc fashion (none,
+ # Public, Internal, Deprecated)
+
+ def sort_sections
+ titles = @sections.map { |title, _| title }
+
+ if titles.length > 1 and
+ TOMDOC_TITLES_SORT ==
+ (titles | TOMDOC_TITLES).sort_by { |title| title.to_s } then
+ @sections.values_at(*TOMDOC_TITLES).compact
+ else
+ @sections.sort_by { |title, _|
+ title.to_s
+ }.map { |_, section|
+ section
+ }
+ end
+ end
+
+ def to_s # :nodoc:
+ "#{self.class.name} #{self.full_name}"
+ end
+
+ ##
+ # Return the TopLevel that owns us
+ #--
+ # FIXME we can be 'owned' by several TopLevel (see #record_location &
+ # #in_files)
+
+ def top_level
+ return @top_level if defined? @top_level
+ @top_level = self
+ @top_level = @top_level.parent until RDoc::TopLevel === @top_level
+ @top_level
+ end
+
+ ##
+ # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
+
+ def upgrade_to_class mod, class_type, enclosing
+ enclosing.modules_hash.delete mod.name
+
+ klass = RDoc::ClassModule.from_module class_type, mod
+ klass.store = @store
+
+ # if it was there, then we keep it even if done_documenting
+ @store.classes_hash[mod.full_name] = klass
+ enclosing.classes_hash[mod.name] = klass
+
+ klass
+ end
+
+ autoload :Section, 'rdoc/context/section'
+
+end
diff --git a/lib/rdoc/context/section.rb b/lib/rdoc/context/section.rb
new file mode 100644
index 0000000000..11f9ceaf87
--- /dev/null
+++ b/lib/rdoc/context/section.rb
@@ -0,0 +1,245 @@
+# frozen_string_literal: true
+##
+# A section of documentation like:
+#
+# # :section: The title
+# # The body
+#
+# Sections can be referenced multiple times and will be collapsed into a
+# single section.
+
+class RDoc::Context::Section
+
+ include RDoc::Text
+
+ MARSHAL_VERSION = 0 # :nodoc:
+
+ ##
+ # Section comment
+
+ attr_reader :comment
+
+ ##
+ # Section comments
+
+ attr_reader :comments
+
+ ##
+ # Context this Section lives in
+
+ attr_reader :parent
+
+ ##
+ # Section title
+
+ attr_reader :title
+
+ @@sequence = "SEC00000"
+
+ ##
+ # Creates a new section with +title+ and +comment+
+
+ def initialize parent, title, comment
+ @parent = parent
+ @title = title ? title.strip : title
+
+ @@sequence = @@sequence.succ
+ @sequence = @@sequence.dup
+
+ @comments = []
+
+ add_comment comment
+ end
+
+ ##
+ # Sections are equal when they have the same #title
+
+ def == other
+ self.class === other and @title == other.title
+ end
+
+ alias eql? ==
+
+ ##
+ # Adds +comment+ to this section
+
+ def add_comment comment
+ comment = extract_comment comment
+
+ return if comment.empty?
+
+ case comment
+ when RDoc::Comment then
+ @comments << comment
+ when RDoc::Markup::Document then
+ @comments.concat comment.parts
+ when Array then
+ @comments.concat comment
+ else
+ raise TypeError, "unknown comment type: #{comment.inspect}"
+ end
+ end
+
+ ##
+ # Anchor reference for linking to this section
+
+ def aref
+ title = @title || '[untitled]'
+
+ CGI.escape(title).gsub('%', '-').sub(/^-/, '')
+ end
+
+ ##
+ # Extracts the comment for this section from the original comment block.
+ # If the first line contains :section:, strip it and use the rest.
+ # Otherwise remove lines up to the line containing :section:, and look
+ # for those lines again at the end and remove them. This lets us write
+ #
+ # # :section: The title
+ # # The body
+
+ def extract_comment comment
+ case comment
+ when Array then
+ comment.map do |c|
+ extract_comment c
+ end
+ when nil
+ RDoc::Comment.new ''
+ when RDoc::Comment then
+ if comment.text =~ /^#[ \t]*:section:.*\n/ then
+ start = $`
+ rest = $'
+
+ comment.text = if start.empty? then
+ rest
+ else
+ rest.sub(/#{start.chomp}\Z/, '')
+ end
+ end
+
+ comment
+ when RDoc::Markup::Document then
+ comment
+ else
+ raise TypeError, "unknown comment #{comment.inspect}"
+ end
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %p>" % [self.class, object_id, title]
+ end
+
+ def hash # :nodoc:
+ @title.hash
+ end
+
+ ##
+ # The files comments in this section come from
+
+ def in_files
+ return [] if @comments.empty?
+
+ case @comments
+ when Array then
+ @comments.map do |comment|
+ comment.file
+ end
+ when RDoc::Markup::Document then
+ @comment.parts.map do |document|
+ document.file
+ end
+ else
+ raise RDoc::Error, "BUG: unknown comment class #{@comments.class}"
+ end
+ end
+
+ ##
+ # Serializes this Section. The title and parsed comment are saved, but not
+ # the section parent which must be restored manually.
+
+ def marshal_dump
+ [
+ MARSHAL_VERSION,
+ @title,
+ parse,
+ ]
+ end
+
+ ##
+ # De-serializes this Section. The section parent must be restored manually.
+
+ def marshal_load array
+ @parent = nil
+
+ @title = array[1]
+ @comments = array[2]
+ end
+
+ ##
+ # Parses +comment_location+ into an RDoc::Markup::Document composed of
+ # multiple RDoc::Markup::Documents with their file set.
+
+ def parse
+ case @comments
+ when String then
+ super
+ when Array then
+ docs = @comments.map do |comment, location|
+ doc = super comment
+ doc.file = location if location
+ doc
+ end
+
+ RDoc::Markup::Document.new(*docs)
+ when RDoc::Comment then
+ doc = super @comments.text, comments.format
+ doc.file = @comments.location
+ doc
+ when RDoc::Markup::Document then
+ return @comments
+ else
+ raise ArgumentError, "unknown comment class #{comments.class}"
+ end
+ end
+
+ ##
+ # The section's title, or 'Top Section' if the title is nil.
+ #
+ # This is used by the table of contents template so the name is silly.
+
+ def plain_html
+ @title || 'Top Section'
+ end
+
+ ##
+ # Removes a comment from this section if it is from the same file as
+ # +comment+
+
+ def remove_comment comment
+ return if @comments.empty?
+
+ case @comments
+ when Array then
+ @comments.delete_if do |my_comment|
+ my_comment.file == comment.file
+ end
+ when RDoc::Markup::Document then
+ @comments.parts.delete_if do |document|
+ document.file == comment.file.name
+ end
+ else
+ raise RDoc::Error, "BUG: unknown comment class #{@comments.class}"
+ end
+ end
+
+ ##
+ # Section sequence number (deprecated)
+
+ def sequence
+ warn "RDoc::Context::Section#sequence is deprecated, use #aref"
+ @sequence
+ end
+
+end
+
diff --git a/lib/rdoc/cross_reference.rb b/lib/rdoc/cross_reference.rb
new file mode 100644
index 0000000000..d76ebaf2d0
--- /dev/null
+++ b/lib/rdoc/cross_reference.rb
@@ -0,0 +1,184 @@
+# frozen_string_literal: true
+##
+# RDoc::CrossReference is a reusable way to create cross references for names.
+
+class RDoc::CrossReference
+
+ ##
+ # Regular expression to match class references
+ #
+ # 1. There can be a '\\' in front of text to suppress the cross-reference
+ # 2. There can be a '::' in front of class names to reference from the
+ # top-level namespace.
+ # 3. The method can be followed by parenthesis (not recommended)
+
+ CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
+
+ ##
+ # Regular expression to match method references.
+ #
+ # See CLASS_REGEXP_STR
+
+ METHOD_REGEXP_STR = '([a-z]\w*[!?=]?|%|===|\[\]=?|<<|>>)(?:\([\w.+*/=<>-]*\))?'
+
+ ##
+ # Regular expressions matching text that should potentially have
+ # cross-reference links generated are passed to add_special. Note that
+ # these expressions are meant to pick up text for which cross-references
+ # have been suppressed, since the suppression characters are removed by the
+ # code that is triggered.
+
+ CROSSREF_REGEXP = /(?:^|\s)
+ (
+ (?:
+ # A::B::C.meth
+ #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
+
+ # Stand-alone method (preceded by a #)
+ | \\?\##{METHOD_REGEXP_STR}
+
+ # Stand-alone method (preceded by ::)
+ | ::#{METHOD_REGEXP_STR}
+
+ # A::B::C
+ # The stuff after CLASS_REGEXP_STR is a
+ # nasty hack. CLASS_REGEXP_STR unfortunately matches
+ # words like dog and cat (these are legal "class"
+ # names in Fortran 95). When a word is flagged as a
+ # potential cross-reference, limitations in the markup
+ # engine suppress other processing, such as typesetting.
+ # This is particularly noticeable for contractions.
+ # In order that words like "can't" not
+ # be flagged as potential cross-references, only
+ # flag potential class cross-references if the character
+ # after the cross-reference is a space, sentence
+ # punctuation, tag start character, or attribute
+ # marker.
+ | #{CLASS_REGEXP_STR}(?=[@\s).?!,;<\000]|\z)
+
+ # Things that look like filenames
+ # The key thing is that there must be at least
+ # one special character (period, slash, or
+ # underscore).
+ | (?:\.\.\/)*[-\/\w]+[_\/.][-\w\/.]+
+
+ # Things that have markup suppressed
+ # Don't process things like '\<' in \<tt>, though.
+ # TODO: including < is a hack, not very satisfying.
+ | \\[^\s<]
+ )
+
+ # labels for headings
+ (?:@[\w+%-]+(?:\.[\w|%-]+)?)?
+ )/x
+
+ ##
+ # Version of CROSSREF_REGEXP used when <tt>--hyperlink-all</tt> is specified.
+
+ ALL_CROSSREF_REGEXP = /
+ (?:^|\s)
+ (
+ (?:
+ # A::B::C.meth
+ #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
+
+ # Stand-alone method
+ | \\?#{METHOD_REGEXP_STR}
+
+ # A::B::C
+ | #{CLASS_REGEXP_STR}(?=[@\s).?!,;<\000]|\z)
+
+ # Things that look like filenames
+ | (?:\.\.\/)*[-\/\w]+[_\/.][-\w\/.]+
+
+ # Things that have markup suppressed
+ | \\[^\s<]
+ )
+
+ # labels for headings
+ (?:@[\w+%-]+)?
+ )/x
+
+ ##
+ # Hash of references that have been looked-up to their replacements
+
+ attr_accessor :seen
+
+ ##
+ # Allows cross-references to be created based on the given +context+
+ # (RDoc::Context).
+
+ def initialize context
+ @context = context
+ @store = context.store
+
+ @seen = {}
+ end
+
+ ##
+ # Returns a reference to +name+.
+ #
+ # If the reference is found and +name+ is not documented +text+ will be
+ # returned. If +name+ is escaped +name+ is returned. If +name+ is not
+ # found +text+ is returned.
+
+ def resolve name, text
+ return @seen[name] if @seen.include? name
+
+ if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then
+ type = $2
+ type = '' if type == '.' # will find either #method or ::method
+ method = "#{type}#{$3}"
+ container = @context.find_symbol_module($1)
+ elsif /^([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then
+ type = $1
+ type = '' if type == '.'
+ method = "#{type}#{$2}"
+ container = @context
+ else
+ container = nil
+ end
+
+ if container then
+ ref = container.find_local_symbol method
+
+ unless ref || RDoc::TopLevel === container then
+ ref = container.find_ancestor_local_symbol method
+ end
+ end
+
+ ref = case name
+ when /^\\(#{CLASS_REGEXP_STR})$/o then
+ @context.find_symbol $1
+ else
+ @context.find_symbol name
+ end unless ref
+
+ # Try a page name
+ ref = @store.page name if not ref and name =~ /^\w+$/
+
+ ref = nil if RDoc::Alias === ref # external alias, can't link to it
+
+ out = if name == '\\' then
+ name
+ elsif name =~ /^\\/ then
+ # we remove the \ only in front of what we know:
+ # other backslashes are treated later, only outside of <tt>
+ ref ? $' : name
+ elsif ref then
+ if ref.display? then
+ ref
+ else
+ text
+ end
+ else
+ text
+ end
+
+ @seen[name] = out
+
+ out
+ end
+
+end
+
diff --git a/lib/rdoc/diagram.rb b/lib/rdoc/diagram.rb
deleted file mode 100644
index 9fdc49c02e..0000000000
--- a/lib/rdoc/diagram.rb
+++ /dev/null
@@ -1,335 +0,0 @@
-# A wonderful hack by to draw package diagrams using the dot package.
-# Originally written by Jah, team Enticla.
-#
-# You must have the V1.7 or later in your path
-# http://www.research.att.com/sw/tools/graphviz/
-
-require "rdoc/dot/dot"
-require 'rdoc/options'
-
-module RDoc
-
- # Draw a set of diagrams representing the modules and classes in the
- # system. We draw one diagram for each file, and one for each toplevel
- # class or module. This means there will be overlap. However, it also
- # means that you'll get better context for objects.
- #
- # To use, simply
- #
- # d = Diagram.new(info) # pass in collection of top level infos
- # d.draw
- #
- # The results will be written to the +dot+ subdirectory. The process
- # also sets the +diagram+ attribute in each object it graphs to
- # the name of the file containing the image. This can be used
- # by output generators to insert images.
-
- class Diagram
-
- FONT = "Arial"
-
- DOT_PATH = "dot"
-
- # Pass in the set of top level objects. The method also creates
- # the subdirectory to hold the images
-
- def initialize(info, options)
- @info = info
- @options = options
- @counter = 0
- File.makedirs(DOT_PATH)
- @diagram_cache = {}
- end
-
- # Draw the diagrams. We traverse the files, drawing a diagram for
- # each. We also traverse each top-level class and module in that
- # file drawing a diagram for these too.
-
- def draw
- unless @options.quiet
- $stderr.print "Diagrams: "
- $stderr.flush
- end
-
- @info.each_with_index do |i, file_count|
- @done_modules = {}
- @local_names = find_names(i)
- @global_names = []
- @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel',
- 'fontname' => FONT,
- 'fontsize' => '8',
- 'bgcolor' => 'lightcyan1',
- 'compound' => 'true')
-
- # it's a little hack %) i'm too lazy to create a separate class
- # for default node
- graph << DOT::DOTNode.new('name' => 'node',
- 'fontname' => FONT,
- 'color' => 'black',
- 'fontsize' => 8)
-
- i.modules.each do |mod|
- draw_module(mod, graph, true, i.file_relative_name)
- end
- add_classes(i, graph, i.file_relative_name)
-
- i.diagram = convert_to_png("f_#{file_count}", graph)
-
- # now go through and document each top level class and
- # module independently
- i.modules.each_with_index do |mod, count|
- @done_modules = {}
- @local_names = find_names(mod)
- @global_names = []
-
- @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel',
- 'fontname' => FONT,
- 'fontsize' => '8',
- 'bgcolor' => 'lightcyan1',
- 'compound' => 'true')
-
- graph << DOT::DOTNode.new('name' => 'node',
- 'fontname' => FONT,
- 'color' => 'black',
- 'fontsize' => 8)
- draw_module(mod, graph, true)
- mod.diagram = convert_to_png("m_#{file_count}_#{count}",
- graph)
- end
- end
- $stderr.puts unless @options.quiet
- end
-
- #######
- private
- #######
-
- def find_names(mod)
- return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
- mod.modules.collect{|m| find_names(m)}.flatten
- end
-
- def find_full_name(name, mod)
- full_name = name.dup
- return full_name if @local_names.include?(full_name)
- mod_path = mod.full_name.split('::')[0..-2]
- unless mod_path.nil?
- until mod_path.empty?
- full_name = mod_path.pop + '::' + full_name
- return full_name if @local_names.include?(full_name)
- end
- end
- return name
- end
-
- def draw_module(mod, graph, toplevel = false, file = nil)
- return if @done_modules[mod.full_name] and not toplevel
-
- @counter += 1
- url = mod.http_url("classes")
- m = DOT::DOTSubgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
- 'label' => mod.name,
- 'fontname' => FONT,
- 'color' => 'blue',
- 'style' => 'filled',
- 'URL' => %{"#{url}"},
- 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
-
- @done_modules[mod.full_name] = m
- add_classes(mod, m, file)
- graph << m
-
- unless mod.includes.empty?
- mod.includes.each do |m|
- m_full_name = find_full_name(m.name, mod)
- if @local_names.include?(m_full_name)
- @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
- 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
- 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
- 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
- else
- unless @global_names.include?(m_full_name)
- path = m_full_name.split("::")
- url = File.join('classes', *path) + ".html"
- @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
- 'shape' => 'box',
- 'label' => "#{m_full_name}",
- 'URL' => %{"#{url}"})
- @global_names << m_full_name
- end
- @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
- 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
- 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
- end
- end
- end
- end
-
- def add_classes(container, graph, file = nil )
-
- use_fileboxes = Options.instance.fileboxes
-
- files = {}
-
- # create dummy node (needed if empty and for module includes)
- if container.full_name
- graph << DOT::DOTNode.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
- 'label' => "",
- 'width' => (container.classes.empty? and
- container.modules.empty?) ?
- '0.75' : '0.01',
- 'height' => '0.01',
- 'shape' => 'plaintext')
- end
- container.classes.each_with_index do |cl, cl_index|
- last_file = cl.in_files[-1].file_relative_name
-
- if use_fileboxes && !files.include?(last_file)
- @counter += 1
- files[last_file] =
- DOT::DOTSubgraph.new('name' => "cluster_#{@counter}",
- 'label' => "#{last_file}",
- 'fontname' => FONT,
- 'color'=>
- last_file == file ? 'red' : 'black')
- end
-
- next if cl.name == 'Object' || cl.name[0,2] == "<<"
-
- url = cl.http_url("classes")
-
- label = cl.name.dup
- if use_fileboxes && cl.in_files.length > 1
- label << '\n[' +
- cl.in_files.collect {|i|
- i.file_relative_name
- }.sort.join( '\n' ) +
- ']'
- end
-
- attrs = {
- 'name' => "#{cl.full_name.gsub( /:/, '_' )}",
- 'fontcolor' => 'black',
- 'style'=>'filled',
- 'color'=>'palegoldenrod',
- 'label' => label,
- 'shape' => 'ellipse',
- 'URL' => %{"#{url}"}
- }
-
- c = DOT::DOTNode.new(attrs)
-
- if use_fileboxes
- files[last_file].push c
- else
- graph << c
- end
- end
-
- if use_fileboxes
- files.each_value do |val|
- graph << val
- end
- end
-
- unless container.classes.empty?
- container.classes.each_with_index do |cl, cl_index|
- cl.includes.each do |m|
- m_full_name = find_full_name(m.name, cl)
- if @local_names.include?(m_full_name)
- @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
- 'to' => "#{cl.full_name.gsub( /:/,'_' )}",
- 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
- else
- unless @global_names.include?(m_full_name)
- path = m_full_name.split("::")
- url = File.join('classes', *path) + ".html"
- @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
- 'shape' => 'box',
- 'label' => "#{m_full_name}",
- 'URL' => %{"#{url}"})
- @global_names << m_full_name
- end
- @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
- 'to' => "#{cl.full_name.gsub( /:/, '_')}")
- end
- end
-
- sclass = cl.superclass
- next if sclass.nil? || sclass == 'Object'
- sclass_full_name = find_full_name(sclass,cl)
- unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
- path = sclass_full_name.split("::")
- url = File.join('classes', *path) + ".html"
- @global_graph << DOT::DOTNode.new(
- 'name' => "#{sclass_full_name.gsub( /:/, '_' )}",
- 'label' => sclass_full_name,
- 'URL' => %{"#{url}"})
- @global_names << sclass_full_name
- end
- @global_graph << DOT::DOTEdge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
- 'to' => "#{cl.full_name.gsub( /:/, '_')}")
- end
- end
-
- container.modules.each do |submod|
- draw_module(submod, graph)
- end
-
- end
-
- def convert_to_png(file_base, graph)
- str = graph.to_s
- return @diagram_cache[str] if @diagram_cache[str]
- op_type = Options.instance.image_format
- dotfile = File.join(DOT_PATH, file_base)
- src = dotfile + ".dot"
- dot = dotfile + "." + op_type
-
- unless @options.quiet
- $stderr.print "."
- $stderr.flush
- end
-
- File.open(src, 'w+' ) do |f|
- f << str << "\n"
- end
-
- system "dot", "-T#{op_type}", src, "-o", dot
-
- # Now construct the imagemap wrapper around
- # that png
-
- ret = wrap_in_image_map(src, dot)
- @diagram_cache[str] = ret
- return ret
- end
-
- # Extract the client-side image map from dot, and use it
- # to generate the imagemap proper. Return the whole
- # <map>..<img> combination, suitable for inclusion on
- # the page
-
- def wrap_in_image_map(src, dot)
- res = %{<map id="map" name="map">\n}
- dot_map = `dot -Tismap #{src}`
- dot_map.each do |area|
- unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
- $stderr.puts "Unexpected output from dot:\n#{area}"
- return nil
- end
-
- xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
- url, area_name = $5, $6
-
- res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
- res << %{ href="#{url}" alt="#{area_name}" />\n}
- end
- res << "</map>\n"
-# map_file = src.sub(/.dot/, '.map')
-# system("dot -Timap #{src} -o #{map_file}")
- res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
- return res
- end
- end
-end
diff --git a/lib/rdoc/dot/dot.rb b/lib/rdoc/dot/dot.rb
deleted file mode 100644
index 6dbb7cb237..0000000000
--- a/lib/rdoc/dot/dot.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-module DOT
-
- # these glogal vars are used to make nice graph source
- $tab = ' '
- $tab2 = $tab * 2
-
- # if we don't like 4 spaces, we can change it any time
- def change_tab( t )
- $tab = t
- $tab2 = t * 2
- end
-
- # options for node declaration
- NODE_OPTS = [
- 'bgcolor',
- 'color',
- 'fontcolor',
- 'fontname',
- 'fontsize',
- 'height',
- 'width',
- 'label',
- 'layer',
- 'rank',
- 'shape',
- 'shapefile',
- 'style',
- 'URL',
- ]
-
- # options for edge declaration
- EDGE_OPTS = [
- 'color',
- 'decorate',
- 'dir',
- 'fontcolor',
- 'fontname',
- 'fontsize',
- 'id',
- 'label',
- 'layer',
- 'lhead',
- 'ltail',
- 'minlen',
- 'style',
- 'weight'
- ]
-
- # options for graph declaration
- GRAPH_OPTS = [
- 'bgcolor',
- 'center',
- 'clusterrank',
- 'color',
- 'compound',
- 'concentrate',
- 'fillcolor',
- 'fontcolor',
- 'fontname',
- 'fontsize',
- 'label',
- 'layerseq',
- 'margin',
- 'mclimit',
- 'nodesep',
- 'nslimit',
- 'ordering',
- 'orientation',
- 'page',
- 'rank',
- 'rankdir',
- 'ranksep',
- 'ratio',
- 'size',
- 'style',
- 'URL'
- ]
-
- # a root class for any element in dot notation
- class DOTSimpleElement
- attr_accessor :name
-
- def initialize( params = {} )
- @label = params['name'] ? params['name'] : ''
- end
-
- def to_s
- @name
- end
- end
-
- # an element that has options ( node, edge or graph )
- class DOTElement < DOTSimpleElement
- #attr_reader :parent
- attr_accessor :name, :options
-
- def initialize( params = {}, option_list = [] )
- super( params )
- @name = params['name'] ? params['name'] : nil
- @parent = params['parent'] ? params['parent'] : nil
- @options = {}
- option_list.each{ |i|
- @options[i] = params[i] if params[i]
- }
- @options['label'] ||= @name if @name != 'node'
- end
-
- def each_option
- @options.each{ |i| yield i }
- end
-
- def each_option_pair
- @options.each_pair{ |key, val| yield key, val }
- end
-
- #def parent=( thing )
- # @parent.delete( self ) if defined?( @parent ) and @parent
- # @parent = thing
- #end
- end
-
-
- # this is used when we build nodes that have shape=record
- # ports don't have options :)
- class DOTPort < DOTSimpleElement
- attr_accessor :label
-
- def initialize( params = {} )
- super( params )
- @name = params['label'] ? params['label'] : ''
- end
- def to_s
- ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
- end
- end
-
- # node element
- class DOTNode < DOTElement
-
- def initialize( params = {}, option_list = NODE_OPTS )
- super( params, option_list )
- @ports = params['ports'] ? params['ports'] : []
- end
-
- def each_port
- @ports.each{ |i| yield i }
- end
-
- def << ( thing )
- @ports << thing
- end
-
- def push ( thing )
- @ports.push( thing )
- end
-
- def pop
- @ports.pop
- end
-
- def to_s( t = '' )
-
- label = @options['shape'] != 'record' && @ports.length == 0 ?
- @options['label'] ?
- t + $tab + "label = \"#{@options['label']}\"\n" :
- '' :
- t + $tab + 'label = "' + " \\\n" +
- t + $tab2 + "#{@options['label']}| \\\n" +
- @ports.collect{ |i|
- t + $tab2 + i.to_s
- }.join( "| \\\n" ) + " \\\n" +
- t + $tab + '"' + "\n"
-
- t + "#{@name} [\n" +
- @options.to_a.collect{ |i|
- i[1] && i[0] != 'label' ?
- t + $tab + "#{i[0]} = #{i[1]}" : nil
- }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
- label +
- t + "]\n"
- end
- end
-
- # subgraph element is the same to graph, but has another header in dot
- # notation
- class DOTSubgraph < DOTElement
-
- def initialize( params = {}, option_list = GRAPH_OPTS )
- super( params, option_list )
- @nodes = params['nodes'] ? params['nodes'] : []
- @dot_string = 'subgraph'
- end
-
- def each_node
- @nodes.each{ |i| yield i }
- end
-
- def << ( thing )
- @nodes << thing
- end
-
- def push( thing )
- @nodes.push( thing )
- end
-
- def pop
- @nodes.pop
- end
-
- def to_s( t = '' )
- hdr = t + "#{@dot_string} #{@name} {\n"
-
- options = @options.to_a.collect{ |name, val|
- val && name != 'label' ?
- t + $tab + "#{name} = #{val}" :
- name ? t + $tab + "#{name} = \"#{val}\"" : nil
- }.compact.join( "\n" ) + "\n"
-
- nodes = @nodes.collect{ |i|
- i.to_s( t + $tab )
- }.join( "\n" ) + "\n"
- hdr + options + nodes + t + "}\n"
- end
- end
-
- # this is graph
- class DOTDigraph < DOTSubgraph
- def initialize( params = {}, option_list = GRAPH_OPTS )
- super( params, option_list )
- @dot_string = 'digraph'
- end
- end
-
- # this is edge
- class DOTEdge < DOTElement
- attr_accessor :from, :to
- def initialize( params = {}, option_list = EDGE_OPTS )
- super( params, option_list )
- @from = params['from'] ? params['from'] : nil
- @to = params['to'] ? params['to'] : nil
- end
-
- def to_s( t = '' )
- t + "#{@from} -> #{to} [\n" +
- @options.to_a.collect{ |i|
- i[1] && i[0] != 'label' ?
- t + $tab + "#{i[0]} = #{i[1]}" :
- i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
- }.compact.join( "\n" ) + "\n" + t + "]\n"
- end
- end
-end
-
-
-
diff --git a/lib/rdoc/encoding.rb b/lib/rdoc/encoding.rb
new file mode 100644
index 0000000000..54ecd89816
--- /dev/null
+++ b/lib/rdoc/encoding.rb
@@ -0,0 +1,130 @@
+# coding: US-ASCII
+# frozen_string_literal: true
+
+##
+# This class is a wrapper around File IO and Encoding that helps RDoc load
+# files and convert them to the correct encoding.
+
+module RDoc::Encoding
+
+ ##
+ # Reads the contents of +filename+ and handles any encoding directives in
+ # the file.
+ #
+ # The content will be converted to the +encoding+. If the file cannot be
+ # converted a warning will be printed and nil will be returned.
+ #
+ # If +force_transcode+ is true the document will be transcoded and any
+ # unknown character in the target encoding will be replaced with '?'
+
+ def self.read_file filename, encoding, force_transcode = false
+ content = open filename, "rb" do |f| f.read end
+ content.gsub!("\r\n", "\n") if RUBY_PLATFORM =~ /mswin|mingw/
+
+ utf8 = content.sub!(/\A\xef\xbb\xbf/, '')
+
+ content = RDoc::Encoding.set_encoding content
+
+ begin
+ encoding ||= Encoding.default_external
+ orig_encoding = content.encoding
+
+ if not orig_encoding.ascii_compatible? then
+ content = content.encode encoding
+ elsif utf8 then
+ content = RDoc::Encoding.change_encoding content, Encoding::UTF_8
+ content = content.encode encoding
+ else
+ # assume the content is in our output encoding
+ content = RDoc::Encoding.change_encoding content, encoding
+ end
+
+ unless content.valid_encoding? then
+ # revert and try to transcode
+ content = RDoc::Encoding.change_encoding content, orig_encoding
+ content = content.encode encoding
+ end
+
+ unless content.valid_encoding? then
+ warn "unable to convert #{filename} to #{encoding}, skipping"
+ content = nil
+ end
+ rescue Encoding::InvalidByteSequenceError,
+ Encoding::UndefinedConversionError => e
+ if force_transcode then
+ content = RDoc::Encoding.change_encoding content, orig_encoding
+ content = content.encode(encoding,
+ :invalid => :replace,
+ :undef => :replace,
+ :replace => '?')
+ return content
+ else
+ warn "unable to convert #{e.message} for #{filename}, skipping"
+ return nil
+ end
+ end
+
+ content
+ rescue ArgumentError => e
+ raise unless e.message =~ /unknown encoding name - (.*)/
+ warn "unknown encoding name \"#{$1}\" for #{filename}, skipping"
+ nil
+ rescue Errno::EISDIR, Errno::ENOENT
+ nil
+ end
+
+ def self.remove_frozen_string_literal string
+ string =~ /\A(?:#!.*\n)?(.*\n)/
+ first_line = $1
+
+ if first_line =~ /\A# +frozen[-_]string[-_]literal[=:].+$/i
+ string = string.sub first_line, ''
+ end
+
+ string
+ end
+
+ ##
+ # Sets the encoding of +string+ based on the magic comment
+
+ def self.set_encoding string
+ string = remove_frozen_string_literal string
+
+ string =~ /\A(?:#!.*\n)?(.*\n)/
+
+ first_line = $1
+
+ name = case first_line
+ when /^<\?xml[^?]*encoding=(["'])(.*?)\1/ then $2
+ when /\b(?:en)?coding[=:]\s*([^\s;]+)/i then $1
+ else return string
+ end
+
+ string = string.sub first_line, ''
+
+ string = remove_frozen_string_literal string
+
+ enc = Encoding.find name
+ string = RDoc::Encoding.change_encoding string, enc if enc
+
+ string
+ end
+
+ ##
+ # Changes encoding based on +encoding+ without converting and returns new
+ # string
+
+ def self.change_encoding text, encoding
+ if text.kind_of? RDoc::Comment
+ text.encode! encoding
+ else
+ # TODO: Remove this condition after Ruby 2.2 EOL
+ if RUBY_VERSION < '2.3.0'
+ text.force_encoding encoding
+ else
+ String.new text, encoding: encoding
+ end
+ end
+ end
+
+end
diff --git a/lib/rdoc/erb_partial.rb b/lib/rdoc/erb_partial.rb
new file mode 100644
index 0000000000..8dc2c46013
--- /dev/null
+++ b/lib/rdoc/erb_partial.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+##
+# Allows an ERB template to be rendered in the context (binding) of an
+# existing ERB template evaluation.
+
+class RDoc::ERBPartial < ERB
+
+ ##
+ # Overrides +compiler+ startup to set the +eoutvar+ to an empty string only
+ # if it isn't already set.
+
+ def set_eoutvar compiler, eoutvar = '_erbout'
+ super
+
+ compiler.pre_cmd = ["#{eoutvar} ||= ''"]
+ end
+
+end
+
diff --git a/lib/rdoc/erbio.rb b/lib/rdoc/erbio.rb
new file mode 100644
index 0000000000..42ce895fb3
--- /dev/null
+++ b/lib/rdoc/erbio.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+require 'erb'
+
+##
+# A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson
+# and Masatoshi SEKI.
+#
+# To use:
+#
+# erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil
+#
+# open 'hello.txt', 'w' do |io|
+# erbio.result binding
+# end
+#
+# Note that binding must enclose the io you wish to output on.
+
+class RDoc::ERBIO < ERB
+
+ ##
+ # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize
+
+ def initialize str, safe_level = nil, trim_mode = nil, eoutvar = 'io'
+ super
+ end
+
+ ##
+ # Instructs +compiler+ how to write to +io_variable+
+
+ def set_eoutvar compiler, io_variable
+ compiler.put_cmd = "#{io_variable}.write"
+ compiler.insert_cmd = "#{io_variable}.write"
+ compiler.pre_cmd = []
+ compiler.post_cmd = []
+ end
+
+end
+
diff --git a/lib/rdoc/extend.rb b/lib/rdoc/extend.rb
new file mode 100644
index 0000000000..e1b182902e
--- /dev/null
+++ b/lib/rdoc/extend.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+##
+# A Module extension to a class with \#extend
+#
+# RDoc::Extend.new 'Enumerable', 'comment ...'
+
+class RDoc::Extend < RDoc::Mixin
+
+end
+
diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb
new file mode 100644
index 0000000000..340dcbf7ae
--- /dev/null
+++ b/lib/rdoc/generator.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+##
+# RDoc uses generators to turn parsed source code in the form of an
+# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML
+# generator RDoc::Generator::Darkfish and an ri data generator
+# RDoc::Generator::RI.
+#
+# == Registering a Generator
+#
+# Generators are registered by calling RDoc::RDoc.add_generator with the class
+# of the generator:
+#
+# class My::Awesome::Generator
+# RDoc::RDoc.add_generator self
+# end
+#
+# == Adding Options to +rdoc+
+#
+# Before option processing in +rdoc+, RDoc::Options will call ::setup_options
+# on the generator class with an RDoc::Options instance. The generator can
+# use RDoc::Options#option_parser to add command-line options to the +rdoc+
+# tool. See RDoc::Options@Custom+Options for an example and see OptionParser
+# for details on how to add options.
+#
+# You can extend the RDoc::Options instance with additional accessors for your
+# generator.
+#
+# == Generator Instantiation
+#
+# After parsing, RDoc::RDoc will instantiate a generator by calling
+# #initialize with an RDoc::Store instance and an RDoc::Options instance.
+#
+# The RDoc::Store instance holds documentation for parsed source code. In
+# RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading
+# a generator from RDoc 3 and earlier you should only need to replace
+# RDoc::TopLevel with the store instance.
+#
+# RDoc will then call #generate on the generator instance. You can use the
+# various methods on RDoc::Store and in the RDoc::CodeObject tree to create
+# your desired output format.
+
+module RDoc::Generator
+
+ autoload :Markup, 'rdoc/generator/markup'
+
+ autoload :Darkfish, 'rdoc/generator/darkfish'
+ autoload :JsonIndex, 'rdoc/generator/json_index'
+ autoload :RI, 'rdoc/generator/ri'
+ autoload :POT, 'rdoc/generator/pot'
+
+end
diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
new file mode 100644
index 0000000000..bf4eb1f530
--- /dev/null
+++ b/lib/rdoc/generator/darkfish.rb
@@ -0,0 +1,786 @@
+# frozen_string_literal: true
+# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
+
+require 'erb'
+require 'fileutils'
+require 'pathname'
+require 'rdoc/generator/markup'
+
+##
+# Darkfish RDoc HTML Generator
+#
+# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
+#
+# == Author/s
+# * Michael Granger (ged@FaerieMUD.org)
+#
+# == Contributors
+# * Mahlon E. Smith (mahlon@martini.nu)
+# * Eric Hodel (drbrain@segment7.net)
+#
+# == License
+#
+# Copyright (c) 2007, 2008, Michael Granger. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of the author/s, nor the names of the project's
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# == Attributions
+#
+# Darkfish uses the {Silk Icons}[http://www.famfamfam.com/lab/icons/silk/] set
+# by Mark James.
+
+class RDoc::Generator::Darkfish
+
+ RDoc::RDoc.add_generator self
+
+ include ERB::Util
+
+ ##
+ # Stylesheets, fonts, etc. that are included in RDoc.
+
+ BUILTIN_STYLE_ITEMS = # :nodoc:
+ %w[
+ css/fonts.css
+ fonts/Lato-Light.ttf
+ fonts/Lato-LightItalic.ttf
+ fonts/Lato-Regular.ttf
+ fonts/Lato-RegularItalic.ttf
+ fonts/SourceCodePro-Bold.ttf
+ fonts/SourceCodePro-Regular.ttf
+ css/rdoc.css
+ ]
+
+ ##
+ # Path to this file's parent directory. Used to find templates and other
+ # resources.
+
+ GENERATOR_DIR = File.join 'rdoc', 'generator'
+
+ ##
+ # Release Version
+
+ VERSION = '3'
+
+ ##
+ # Description of this generator
+
+ DESCRIPTION = 'HTML generator, written by Michael Granger'
+
+ ##
+ # The relative path to style sheets and javascript. By default this is set
+ # the same as the rel_prefix.
+
+ attr_accessor :asset_rel_path
+
+ ##
+ # The path to generate files into, combined with <tt>--op</tt> from the
+ # options for a full path.
+
+ attr_reader :base_dir
+
+ ##
+ # Classes and modules to be used by this generator, not necessarily
+ # displayed. See also #modsort
+
+ attr_reader :classes
+
+ ##
+ # No files will be written when dry_run is true.
+
+ attr_accessor :dry_run
+
+ ##
+ # When false the generate methods return a String instead of writing to a
+ # file. The default is true.
+
+ attr_accessor :file_output
+
+ ##
+ # Files to be displayed by this generator
+
+ attr_reader :files
+
+ ##
+ # The JSON index generator for this Darkfish generator
+
+ attr_reader :json_index
+
+ ##
+ # Methods to be displayed by this generator
+
+ attr_reader :methods
+
+ ##
+ # Sorted list of classes and modules to be displayed by this generator
+
+ attr_reader :modsort
+
+ ##
+ # The RDoc::Store that is the source of the generated content
+
+ attr_reader :store
+
+ ##
+ # The directory where the template files live
+
+ attr_reader :template_dir # :nodoc:
+
+ ##
+ # The output directory
+
+ attr_reader :outputdir
+
+ ##
+ # Initialize a few instance variables before we start
+
+ def initialize store, options
+ @store = store
+ @options = options
+
+ @asset_rel_path = ''
+ @base_dir = Pathname.pwd.expand_path
+ @dry_run = @options.dry_run
+ @file_output = true
+ @template_dir = Pathname.new options.template_dir
+ @template_cache = {}
+
+ @classes = nil
+ @context = nil
+ @files = nil
+ @methods = nil
+ @modsort = nil
+
+ @json_index = RDoc::Generator::JsonIndex.new self, options
+ end
+
+ ##
+ # Output progress information if debugging is enabled
+
+ def debug_msg *msg
+ return unless $DEBUG_RDOC
+ $stderr.puts(*msg)
+ end
+
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
+
+ def class_dir
+ nil
+ end
+
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
+
+ def file_dir
+ nil
+ end
+
+ ##
+ # Create the directories the generated docs will live in if they don't
+ # already exist.
+
+ def gen_sub_directories
+ @outputdir.mkpath
+ end
+
+ ##
+ # Copy over the stylesheet into the appropriate place in the output
+ # directory.
+
+ def write_style_sheet
+ debug_msg "Copying static files"
+ options = { :verbose => $DEBUG_RDOC, :noop => @dry_run }
+
+ BUILTIN_STYLE_ITEMS.each do |item|
+ install_rdoc_static_file @template_dir + item, "./#{item}", options
+ end
+
+ @options.template_stylesheets.each do |stylesheet|
+ FileUtils.cp stylesheet, '.', options
+ end
+
+ Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
+ next if File.directory? path
+ next if File.basename(path) =~ /^\./
+
+ dst = Pathname.new(path).relative_path_from @template_dir
+
+ install_rdoc_static_file @template_dir + path, dst, options
+ end
+ end
+
+ ##
+ # Build the initial indices and output objects based on an array of TopLevel
+ # objects containing the extracted information.
+
+ def generate
+ setup
+
+ write_style_sheet
+ generate_index
+ generate_class_files
+ generate_file_files
+ generate_table_of_contents
+ @json_index.generate
+ @json_index.generate_gzipped
+
+ copy_static
+
+ rescue => e
+ debug_msg "%s: %s\n %s" % [
+ e.class.name, e.message, e.backtrace.join("\n ")
+ ]
+
+ raise
+ end
+
+ ##
+ # Copies static files from the static_path into the output directory
+
+ def copy_static
+ return if @options.static_path.empty?
+
+ fu_options = { :verbose => $DEBUG_RDOC, :noop => @dry_run }
+
+ @options.static_path.each do |path|
+ unless File.directory? path then
+ FileUtils.install path, @outputdir, fu_options.merge(:mode => 0644)
+ next
+ end
+
+ Dir.chdir path do
+ Dir[File.join('**', '*')].each do |entry|
+ dest_file = @outputdir + entry
+
+ if File.directory? entry then
+ FileUtils.mkdir_p entry, fu_options
+ else
+ FileUtils.install entry, dest_file, fu_options.merge(:mode => 0644)
+ end
+ end
+ end
+ end
+ end
+
+ ##
+ # Return a list of the documented modules sorted by salience first, then
+ # by name.
+
+ def get_sorted_module_list classes
+ classes.select do |klass|
+ klass.display?
+ end.sort
+ end
+
+ ##
+ # Generate an index page which lists all the classes which are documented.
+
+ def generate_index
+ setup
+
+ template_file = @template_dir + 'index.rhtml'
+ return unless template_file.exist?
+
+ debug_msg "Rendering the index page..."
+
+ out_file = @base_dir + @options.op_dir + 'index.html'
+ rel_prefix = @outputdir.relative_path_from out_file.dirname
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+
+ @title = @options.title
+
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
+ rescue => e
+ error = RDoc::Error.new \
+ "error generating index.html: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ ##
+ # Generates a class file for +klass+
+
+ def generate_class klass, template_file = nil
+ setup
+
+ current = klass
+
+ template_file ||= @template_dir + 'class.rhtml'
+
+ debug_msg " working on %s (%s)" % [klass.full_name, klass.path]
+ out_file = @outputdir + klass.path
+ rel_prefix = @outputdir.relative_path_from out_file.dirname
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+ svninfo = get_svninfo(current)
+
+ @title = "#{klass.type} #{klass.full_name} - #{@options.title}"
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here.local_variable_set(:svninfo, svninfo)
+ here
+ end
+ end
+
+ ##
+ # Generate a documentation file for each class and module
+
+ def generate_class_files
+ setup
+
+ template_file = @template_dir + 'class.rhtml'
+ template_file = @template_dir + 'classpage.rhtml' unless
+ template_file.exist?
+ return unless template_file.exist?
+ debug_msg "Generating class documentation in #{@outputdir}"
+
+ current = nil
+
+ @classes.each do |klass|
+ current = klass
+
+ generate_class klass, template_file
+ end
+ rescue => e
+ error = RDoc::Error.new \
+ "error generating #{current.path}: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ ##
+ # Generate a documentation file for each file
+
+ def generate_file_files
+ setup
+
+ page_file = @template_dir + 'page.rhtml'
+ fileinfo_file = @template_dir + 'fileinfo.rhtml'
+
+ # for legacy templates
+ filepage_file = @template_dir + 'filepage.rhtml' unless
+ page_file.exist? or fileinfo_file.exist?
+
+ return unless
+ page_file.exist? or fileinfo_file.exist? or filepage_file.exist?
+
+ debug_msg "Generating file documentation in #{@outputdir}"
+
+ out_file = nil
+ current = nil
+
+ @files.each do |file|
+ current = file
+
+ if file.text? and page_file.exist? then
+ generate_page file
+ next
+ end
+
+ template_file = nil
+ out_file = @outputdir + file.path
+ debug_msg " working on %s (%s)" % [file.full_name, out_file]
+ rel_prefix = @outputdir.relative_path_from out_file.dirname
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+
+ unless filepage_file then
+ if file.text? then
+ next unless page_file.exist?
+ template_file = page_file
+ @title = file.page_name
+ else
+ next unless fileinfo_file.exist?
+ template_file = fileinfo_file
+ @title = "File: #{file.base_name}"
+ end
+ end
+
+ @title += " - #{@options.title}"
+ template_file ||= filepage_file
+
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here.local_variable_set(:current, current)
+ here
+ end
+ end
+ rescue => e
+ error =
+ RDoc::Error.new "error generating #{out_file}: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ ##
+ # Generate a page file for +file+
+
+ def generate_page file
+ setup
+
+ template_file = @template_dir + 'page.rhtml'
+
+ out_file = @outputdir + file.path
+ debug_msg " working on %s (%s)" % [file.full_name, out_file]
+ rel_prefix = @outputdir.relative_path_from out_file.dirname
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ current = file
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+
+ @title = "#{file.page_name} - #{@options.title}"
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:current, current)
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
+ end
+
+ ##
+ # Generates the 404 page for the RDoc servlet
+
+ def generate_servlet_not_found message
+ setup
+
+ template_file = @template_dir + 'servlet_not_found.rhtml'
+ return unless template_file.exist?
+
+ debug_msg "Rendering the servlet 404 Not Found page..."
+
+ rel_prefix = rel_prefix = ''
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ asset_rel_prefix = ''
+
+ @title = 'Not Found'
+
+ render_template template_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
+ rescue => e
+ error = RDoc::Error.new \
+ "error generating servlet_not_found: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ ##
+ # Generates the servlet root page for the RDoc servlet
+
+ def generate_servlet_root installed
+ setup
+
+ template_file = @template_dir + 'servlet_root.rhtml'
+ return unless template_file.exist?
+
+ debug_msg 'Rendering the servlet root page...'
+
+ rel_prefix = '.'
+ asset_rel_prefix = rel_prefix
+ search_index_rel_prefix = asset_rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ @title = 'Local RDoc Documentation'
+
+ render_template template_file do |io| binding end
+ rescue => e
+ error = RDoc::Error.new \
+ "error generating servlet_root: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ ##
+ # Generate an index page which lists all the classes which are documented.
+
+ def generate_table_of_contents
+ setup
+
+ template_file = @template_dir + 'table_of_contents.rhtml'
+ return unless template_file.exist?
+
+ debug_msg "Rendering the Table of Contents..."
+
+ out_file = @outputdir + 'table_of_contents.html'
+ rel_prefix = @outputdir.relative_path_from out_file.dirname
+ search_index_rel_prefix = rel_prefix
+ search_index_rel_prefix += @asset_rel_path if @file_output
+
+ asset_rel_prefix = rel_prefix + @asset_rel_path
+
+ @title = "Table of Contents - #{@options.title}"
+
+ render_template template_file, out_file do |io|
+ here = binding
+ # suppress 1.9.3 warning
+ here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
+ here
+ end
+ rescue => e
+ error = RDoc::Error.new \
+ "error generating table_of_contents.html: #{e.message} (#{e.class})"
+ error.set_backtrace e.backtrace
+
+ raise error
+ end
+
+ def install_rdoc_static_file source, destination, options # :nodoc:
+ return unless source.exist?
+
+ begin
+ FileUtils.mkdir_p File.dirname(destination), options
+
+ begin
+ FileUtils.ln source, destination, options
+ rescue Errno::EEXIST
+ FileUtils.rm destination
+ retry
+ end
+ rescue
+ FileUtils.cp source, destination, options
+ end
+ end
+
+ ##
+ # Prepares for generation of output from the current directory
+
+ def setup
+ return if instance_variable_defined? :@outputdir
+
+ @outputdir = Pathname.new(@options.op_dir).expand_path @base_dir
+
+ return unless @store
+
+ @classes = @store.all_classes_and_modules.sort
+ @files = @store.all_files.sort
+ @methods = @classes.map { |m| m.method_list }.flatten.sort
+ @modsort = get_sorted_module_list @classes
+ end
+
+ ##
+ # Return a string describing the amount of time in the given number of
+ # seconds in terms a human can understand easily.
+
+ def time_delta_string seconds
+ return 'less than a minute' if seconds < 60
+ return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if
+ seconds < 3000 # 50 minutes
+ return 'about one hour' if seconds < 5400 # 90 minutes
+ return "#{seconds / 3600} hours" if seconds < 64800 # 18 hours
+ return 'one day' if seconds < 86400 # 1 day
+ return 'about one day' if seconds < 172800 # 2 days
+ return "#{seconds / 86400} days" if seconds < 604800 # 1 week
+ return 'about one week' if seconds < 1209600 # 2 week
+ return "#{seconds / 604800} weeks" if seconds < 7257600 # 3 months
+ return "#{seconds / 2419200} months" if seconds < 31536000 # 1 year
+ return "#{seconds / 31536000} years"
+ end
+
+ # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
+ SVNID_PATTERN = /
+ \$Id:\s
+ (\S+)\s # filename
+ (\d+)\s # rev
+ (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
+ (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
+ (\w+)\s # committer
+ \$$
+ /x
+
+ ##
+ # Try to extract Subversion information out of the first constant whose
+ # value looks like a subversion Id tag. If no matching constant is found,
+ # and empty hash is returned.
+
+ def get_svninfo klass
+ constants = klass.constants or return {}
+
+ constants.find { |c| c.value =~ SVNID_PATTERN } or return {}
+
+ filename, rev, date, time, committer = $~.captures
+ commitdate = Time.parse "#{date} #{time}"
+
+ return {
+ :filename => filename,
+ :rev => Integer(rev),
+ :commitdate => commitdate,
+ :commitdelta => time_delta_string(Time.now - commitdate),
+ :committer => committer,
+ }
+ end
+
+ ##
+ # Creates a template from its components and the +body_file+.
+ #
+ # For backwards compatibility, if +body_file+ contains "<html" the body is
+ # used directly.
+
+ def assemble_template body_file
+ body = body_file.read
+ return body if body =~ /<html/
+
+ head_file = @template_dir + '_head.rhtml'
+ footer_file = @template_dir + '_footer.rhtml'
+
+ <<-TEMPLATE
+<!DOCTYPE html>
+
+<html>
+<head>
+#{head_file.read}
+
+#{body}
+
+#{footer_file.read}
+ TEMPLATE
+ end
+
+ ##
+ # Renders the ERb contained in +file_name+ relative to the template
+ # directory and returns the result based on the current context.
+
+ def render file_name
+ template_file = @template_dir + file_name
+
+ template = template_for template_file, false, RDoc::ERBPartial
+
+ template.filename = template_file.to_s
+
+ template.result @context
+ end
+
+ ##
+ # Load and render the erb template in the given +template_file+ and write
+ # it out to +out_file+.
+ #
+ # Both +template_file+ and +out_file+ should be Pathname-like objects.
+ #
+ # An io will be yielded which must be captured by binding in the caller.
+
+ def render_template template_file, out_file = nil # :yield: io
+ io_output = out_file && !@dry_run && @file_output
+ erb_klass = io_output ? RDoc::ERBIO : ERB
+
+ template = template_for template_file, true, erb_klass
+
+ if io_output then
+ debug_msg "Outputting to %s" % [out_file.expand_path]
+
+ out_file.dirname.mkpath
+ out_file.open 'w', 0644 do |io|
+ io.set_encoding @options.encoding
+
+ @context = yield io
+
+ template_result template, @context, template_file
+ end
+ else
+ @context = yield nil
+
+ output = template_result template, @context, template_file
+
+ debug_msg " would have written %d characters to %s" % [
+ output.length, out_file.expand_path
+ ] if @dry_run
+
+ output
+ end
+ end
+
+ ##
+ # Creates the result for +template+ with +context+. If an error is raised a
+ # Pathname +template_file+ will indicate the file where the error occurred.
+
+ def template_result template, context, template_file
+ template.filename = template_file.to_s
+ template.result context
+ rescue NoMethodError => e
+ raise RDoc::Error, "Error while evaluating %s: %s" % [
+ template_file.expand_path,
+ e.message,
+ ], e.backtrace
+ end
+
+ ##
+ # Retrieves a cache template for +file+, if present, or fills the cache.
+
+ def template_for file, page = true, klass = ERB
+ template = @template_cache[file]
+
+ return template if template
+
+ if page then
+ template = assemble_template file
+ erbout = 'io'
+ else
+ template = file.read
+ template = template.encode @options.encoding
+
+ file_var = File.basename(file).sub(/\..*/, '')
+
+ erbout = "_erbout_#{file_var}"
+ end
+
+ template = klass.new template, nil, '<>', erbout
+ @template_cache[file] = template
+ template
+ end
+
+end
diff --git a/lib/rdoc/generator/json_index.rb b/lib/rdoc/generator/json_index.rb
new file mode 100644
index 0000000000..e4cfe967c6
--- /dev/null
+++ b/lib/rdoc/generator/json_index.rb
@@ -0,0 +1,297 @@
+# frozen_string_literal: true
+require 'json'
+begin
+ require 'zlib'
+rescue LoadError
+end
+
+##
+# The JsonIndex generator is designed to complement an HTML generator and
+# produces a JSON search index. This generator is derived from sdoc by
+# Vladimir Kolesnikov and contains verbatim code written by him.
+#
+# This generator is designed to be used with a regular HTML generator:
+#
+# class RDoc::Generator::Darkfish
+# def initialize options
+# # ...
+# @base_dir = Pathname.pwd.expand_path
+#
+# @json_index = RDoc::Generator::JsonIndex.new self, options
+# end
+#
+# def generate
+# # ...
+# @json_index.generate
+# end
+# end
+#
+# == Index Format
+#
+# The index is output as a JSON file assigned to the global variable
+# +search_data+. The structure is:
+#
+# var search_data = {
+# "index": {
+# "searchIndex":
+# ["a", "b", ...],
+# "longSearchIndex":
+# ["a", "a::b", ...],
+# "info": [
+# ["A", "A", "A.html", "", ""],
+# ["B", "A::B", "A::B.html", "", ""],
+# ...
+# ]
+# }
+# }
+#
+# The same item is described across the +searchIndex+, +longSearchIndex+ and
+# +info+ fields. The +searchIndex+ field contains the item's short name, the
+# +longSearchIndex+ field contains the full_name (when appropriate) and the
+# +info+ field contains the item's name, full_name, path, parameters and a
+# snippet of the item's comment.
+#
+# == LICENSE
+#
+# Copyright (c) 2009 Vladimir Kolesnikov
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+class RDoc::Generator::JsonIndex
+
+ include RDoc::Text
+
+ ##
+ # Where the search index lives in the generated output
+
+ SEARCH_INDEX_FILE = File.join 'js', 'search_index.js'
+
+ attr_reader :index # :nodoc:
+
+ ##
+ # Creates a new generator. +parent_generator+ is used to determine the
+ # class_dir and file_dir of links in the output index.
+ #
+ # +options+ are the same options passed to the parent generator.
+
+ def initialize parent_generator, options
+ @parent_generator = parent_generator
+ @store = parent_generator.store
+ @options = options
+
+ @template_dir = File.expand_path '../template/json_index', __FILE__
+ @base_dir = @parent_generator.base_dir
+
+ @classes = nil
+ @files = nil
+ @index = nil
+ end
+
+ ##
+ # Builds the JSON index as a Hash.
+
+ def build_index
+ reset @store.all_files.sort, @store.all_classes_and_modules.sort
+
+ index_classes
+ index_methods
+ index_pages
+
+ { :index => @index }
+ end
+
+ ##
+ # Output progress information if debugging is enabled
+
+ def debug_msg *msg
+ return unless $DEBUG_RDOC
+ $stderr.puts(*msg)
+ end
+
+ ##
+ # Writes the JSON index to disk
+
+ def generate
+ debug_msg "Generating JSON index"
+
+ debug_msg " writing search index to %s" % SEARCH_INDEX_FILE
+ data = build_index
+
+ return if @options.dry_run
+
+ out_dir = @base_dir + @options.op_dir
+ index_file = out_dir + SEARCH_INDEX_FILE
+
+ FileUtils.mkdir_p index_file.dirname, :verbose => $DEBUG_RDOC
+
+ index_file.open 'w', 0644 do |io|
+ io.set_encoding Encoding::UTF_8
+ io.write 'var search_data = '
+
+ JSON.dump data, io, 0
+ end
+
+ Dir.chdir @template_dir do
+ Dir['**/*.js'].each do |source|
+ dest = File.join out_dir, source
+
+ FileUtils.install source, dest, :mode => 0644, :verbose => $DEBUG_RDOC
+ end
+ end
+ end
+
+ ##
+ # Compress the search_index.js file using gzip
+
+ def generate_gzipped
+ return if @options.dry_run or not defined?(Zlib)
+
+ debug_msg "Compressing generated JSON index"
+ out_dir = @base_dir + @options.op_dir
+
+ search_index_file = out_dir + SEARCH_INDEX_FILE
+ outfile = out_dir + "#{search_index_file}.gz"
+
+ debug_msg "Reading the JSON index file from %s" % search_index_file
+ search_index = search_index_file.read(mode: 'r:utf-8')
+
+ debug_msg "Writing gzipped search index to %s" % outfile
+
+ Zlib::GzipWriter.open(outfile) do |gz|
+ gz.mtime = File.mtime(search_index_file)
+ gz.orig_name = search_index_file.basename.to_s
+ gz.write search_index
+ gz.close
+ end
+
+ # GZip the rest of the js files
+ Dir.chdir @template_dir do
+ Dir['**/*.js'].each do |source|
+ dest = out_dir + source
+ outfile = out_dir + "#{dest}.gz"
+
+ debug_msg "Reading the original js file from %s" % dest
+ data = dest.read
+
+ debug_msg "Writing gzipped file to %s" % outfile
+
+ Zlib::GzipWriter.open(outfile) do |gz|
+ gz.mtime = File.mtime(dest)
+ gz.orig_name = dest.basename.to_s
+ gz.write data
+ gz.close
+ end
+ end
+ end
+ end
+
+ ##
+ # Adds classes and modules to the index
+
+ def index_classes
+ debug_msg " generating class search index"
+
+ documented = @classes.uniq.select do |klass|
+ klass.document_self_or_methods
+ end
+
+ documented.each do |klass|
+ debug_msg " #{klass.full_name}"
+ record = klass.search_record
+ @index[:searchIndex] << search_string(record.shift)
+ @index[:longSearchIndex] << search_string(record.shift)
+ @index[:info] << record
+ end
+ end
+
+ ##
+ # Adds methods to the index
+
+ def index_methods
+ debug_msg " generating method search index"
+
+ list = @classes.uniq.map do |klass|
+ klass.method_list
+ end.flatten.sort_by do |method|
+ [method.name, method.parent.full_name]
+ end
+
+ list.each do |method|
+ debug_msg " #{method.full_name}"
+ record = method.search_record
+ @index[:searchIndex] << "#{search_string record.shift}()"
+ @index[:longSearchIndex] << "#{search_string record.shift}()"
+ @index[:info] << record
+ end
+ end
+
+ ##
+ # Adds pages to the index
+
+ def index_pages
+ debug_msg " generating pages search index"
+
+ pages = @files.select do |file|
+ file.text?
+ end
+
+ pages.each do |page|
+ debug_msg " #{page.page_name}"
+ record = page.search_record
+ @index[:searchIndex] << search_string(record.shift)
+ @index[:longSearchIndex] << ''
+ record.shift
+ @index[:info] << record
+ end
+ end
+
+ ##
+ # The directory classes are written to
+
+ def class_dir
+ @parent_generator.class_dir
+ end
+
+ ##
+ # The directory files are written to
+
+ def file_dir
+ @parent_generator.file_dir
+ end
+
+ def reset files, classes # :nodoc:
+ @files = files
+ @classes = classes
+
+ @index = {
+ :searchIndex => [],
+ :longSearchIndex => [],
+ :info => []
+ }
+ end
+
+ ##
+ # Removes whitespace and downcases +string+
+
+ def search_string string
+ string.downcase.gsub(/\s/, '')
+ end
+
+end
diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb
new file mode 100644
index 0000000000..fef982d378
--- /dev/null
+++ b/lib/rdoc/generator/markup.rb
@@ -0,0 +1,170 @@
+# frozen_string_literal: true
+##
+# Handle common RDoc::Markup tasks for various CodeObjects
+#
+# This module is loaded by generators. It allows RDoc's CodeObject tree to
+# avoid loading generator code to improve startup time for +ri+.
+
+module RDoc::Generator::Markup
+
+ ##
+ # Generates a relative URL from this object's path to +target_path+
+
+ def aref_to(target_path)
+ RDoc::Markup::ToHtml.gen_relative_url path, target_path
+ end
+
+ ##
+ # Generates a relative URL from +from_path+ to this object's path
+
+ def as_href(from_path)
+ RDoc::Markup::ToHtml.gen_relative_url from_path, path
+ end
+
+ ##
+ # Handy wrapper for marking up this object's comment
+
+ def description
+ markup @comment
+ end
+
+ ##
+ # Creates an RDoc::Markup::ToHtmlCrossref formatter
+
+ def formatter
+ return @formatter if defined? @formatter
+
+ options = @store.rdoc.options
+ this = RDoc::Context === self ? self : @parent
+
+ @formatter = RDoc::Markup::ToHtmlCrossref.new options, this.path, this
+ @formatter.code_object = self
+ @formatter
+ end
+
+ ##
+ # Build a webcvs URL starting for the given +url+ with +full_path+ appended
+ # as the destination path. If +url+ contains '%s' +full_path+ will be
+ # will replace the %s using sprintf on the +url+.
+
+ def cvs_url(url, full_path)
+ if /%s/ =~ url then
+ sprintf url, full_path
+ else
+ url + full_path
+ end
+ end
+
+end
+
+class RDoc::CodeObject
+
+ include RDoc::Generator::Markup
+
+end
+
+class RDoc::MethodAttr
+
+ @add_line_numbers = false
+
+ class << self
+ ##
+ # Allows controlling whether <tt>#markup_code</tt> adds line numbers to
+ # the source code.
+
+ attr_accessor :add_line_numbers
+ end
+
+ ##
+ # Prepend +src+ with line numbers. Relies on the first line of a source
+ # code listing having:
+ #
+ # # File xxxxx, line dddd
+ #
+ # If it has this comment then line numbers are added to +src+ and the <tt>,
+ # line dddd</tt> portion of the comment is removed.
+
+ def add_line_numbers(src)
+ return unless src.sub!(/\A(.*)(, line (\d+))/, '\1')
+ first = $3.to_i - 1
+ last = first + src.count("\n")
+ size = last.to_s.length
+
+ line = first
+ src.gsub!(/^/) do
+ res = if line == first then
+ " " * (size + 1)
+ else
+ "<span class=\"line-num\">%2$*1$d</span> " % [size, line]
+ end
+
+ line += 1
+ res
+ end
+ end
+
+ ##
+ # Turns the method's token stream into HTML.
+ #
+ # Prepends line numbers if +add_line_numbers+ is true.
+
+ def markup_code
+ return '' unless @token_stream
+
+ src = RDoc::TokenStream.to_html @token_stream
+
+ # dedent the source
+ indent = src.length
+ lines = src.lines.to_a
+ lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment
+ lines.each do |line|
+ if line =~ /^ *(?=\S)/
+ n = $&.length
+ indent = n if n < indent
+ break if n == 0
+ end
+ end
+ src.gsub!(/^#{' ' * indent}/, '') if indent > 0
+
+ add_line_numbers(src) if RDoc::MethodAttr.add_line_numbers
+
+ src
+ end
+
+end
+
+class RDoc::ClassModule
+
+ ##
+ # Handy wrapper for marking up this class or module's comment
+
+ def description
+ markup @comment_location
+ end
+
+end
+
+class RDoc::Context::Section
+
+ include RDoc::Generator::Markup
+
+end
+
+class RDoc::TopLevel
+
+ ##
+ # Returns a URL for this source file on some web repository. Use the -W
+ # command line option to set.
+
+ def cvs_url
+ url = @store.rdoc.options.webcvs
+
+ if /%s/ =~ url then
+ url % @relative_name
+ else
+ url + @relative_name
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/generator/pot.rb b/lib/rdoc/generator/pot.rb
new file mode 100644
index 0000000000..8a1e0b4bd0
--- /dev/null
+++ b/lib/rdoc/generator/pot.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+##
+# Generates a POT file.
+#
+# Here is a translator work flow with the generator.
+#
+# == Create .pot
+#
+# You create .pot file by pot formatter:
+#
+# % rdoc --format pot
+#
+# It generates doc/rdoc.pot.
+#
+# == Create .po
+#
+# You create .po file from doc/rdoc.pot. This operation is needed only
+# the first time. This work flow assumes that you are a translator
+# for Japanese.
+#
+# You create locale/ja/rdoc.po from doc/rdoc.pot. You can use msginit
+# provided by GNU gettext or rmsginit provided by gettext gem. This
+# work flow uses gettext gem because it is more portable than GNU
+# gettext for Rubyists. Gettext gem is implemented by pure Ruby.
+#
+# % gem install gettext
+# % mkdir -p locale/ja
+# % rmsginit --input doc/rdoc.pot --output locale/ja/rdoc.po --locale ja
+#
+# Translate messages in .po
+#
+# You translate messages in .po by a PO file editor. po-mode.el exists
+# for Emacs users. There are some GUI tools such as GTranslator.
+# There are some Web services such as POEditor and Tansifex. You can
+# edit by your favorite text editor because .po is a text file.
+# Generate localized documentation
+#
+# You can generate localized documentation with locale/ja/rdoc.po:
+#
+# % rdoc --locale ja
+#
+# You can find documentation in Japanese in doc/. Yay!
+#
+# == Update translation
+#
+# You need to update translation when your application is added or
+# modified messages.
+#
+# You can update .po by the following command lines:
+#
+# % rdoc --format pot
+# % rmsgmerge --update locale/ja/rdoc.po doc/rdoc.pot
+#
+# You edit locale/ja/rdoc.po to translate new messages.
+
+class RDoc::Generator::POT
+
+ RDoc::RDoc.add_generator self
+
+ ##
+ # Description of this generator
+
+ DESCRIPTION = 'creates .pot file'
+
+ ##
+ # Set up a new .pot generator
+
+ def initialize store, options #:not-new:
+ @options = options
+ @store = store
+ end
+
+ ##
+ # Writes .pot to disk.
+
+ def generate
+ po = extract_messages
+ pot_path = 'rdoc.pot'
+ File.open(pot_path, "w") do |pot|
+ pot.print(po.to_s)
+ end
+ end
+
+ def class_dir
+ nil
+ end
+
+ private
+ def extract_messages
+ extractor = MessageExtractor.new(@store)
+ extractor.extract
+ end
+
+ autoload :MessageExtractor, 'rdoc/generator/pot/message_extractor'
+ autoload :PO, 'rdoc/generator/pot/po'
+ autoload :POEntry, 'rdoc/generator/pot/po_entry'
+
+end
diff --git a/lib/rdoc/generator/pot/message_extractor.rb b/lib/rdoc/generator/pot/message_extractor.rb
new file mode 100644
index 0000000000..313dfd2dc7
--- /dev/null
+++ b/lib/rdoc/generator/pot/message_extractor.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+##
+# Extracts message from RDoc::Store
+
+class RDoc::Generator::POT::MessageExtractor
+
+ ##
+ # Creates a message extractor for +store+.
+
+ def initialize store
+ @store = store
+ @po = RDoc::Generator::POT::PO.new
+ end
+
+ ##
+ # Extracts messages from +store+, stores them into
+ # RDoc::Generator::POT::PO and returns it.
+
+ def extract
+ @store.all_classes_and_modules.each do |klass|
+ extract_from_klass(klass)
+ end
+ @po
+ end
+
+ private
+
+ def extract_from_klass klass
+ extract_text(klass.comment_location, klass.full_name)
+
+ klass.each_section do |section, constants, attributes|
+ extract_text(section.title ,"#{klass.full_name}: section title")
+ section.comments.each do |comment|
+ extract_text(comment, "#{klass.full_name}: #{section.title}")
+ end
+ end
+
+ klass.each_constant do |constant|
+ extract_text(constant.comment, constant.full_name)
+ end
+
+ klass.each_attribute do |attribute|
+ extract_text(attribute.comment, attribute.full_name)
+ end
+
+ klass.each_method do |method|
+ extract_text(method.comment, method.full_name)
+ end
+ end
+
+ def extract_text text, comment, location = nil
+ return if text.nil?
+
+ options = {
+ :extracted_comment => comment,
+ :references => [location].compact,
+ }
+ i18n_text = RDoc::I18n::Text.new(text)
+ i18n_text.extract_messages do |part|
+ @po.add(entry(part[:paragraph], options))
+ end
+ end
+
+ def entry msgid, options
+ RDoc::Generator::POT::POEntry.new(msgid, options)
+ end
+
+end
diff --git a/lib/rdoc/generator/pot/po.rb b/lib/rdoc/generator/pot/po.rb
new file mode 100644
index 0000000000..37d45e5258
--- /dev/null
+++ b/lib/rdoc/generator/pot/po.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+##
+# Generates a PO format text
+
+class RDoc::Generator::POT::PO
+
+ ##
+ # Creates an object that represents PO format.
+
+ def initialize
+ @entries = {}
+ add_header
+ end
+
+ ##
+ # Adds a PO entry to the PO.
+
+ def add entry
+ existing_entry = @entries[entry.msgid]
+ if existing_entry
+ entry = existing_entry.merge(entry)
+ end
+ @entries[entry.msgid] = entry
+ end
+
+ ##
+ # Returns PO format text for the PO.
+
+ def to_s
+ po = ''
+ sort_entries.each do |entry|
+ po += "\n" unless po.empty?
+ po += entry.to_s
+ end
+ po
+ end
+
+ private
+
+ def add_header
+ add(header_entry)
+ end
+
+ def header_entry
+ comment = <<-COMMENT
+SOME DESCRIPTIVE TITLE.
+Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+This file is distributed under the same license as the PACKAGE package.
+FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+ COMMENT
+
+ content = <<-CONTENT
+Project-Id-Version: PACKAGE VERSEION
+Report-Msgid-Bugs-To:
+PO-Revision-Date: YEAR-MO_DA HO:MI+ZONE
+Last-Translator: FULL NAME <EMAIL@ADDRESS>
+Language-Team: LANGUAGE <LL@li.org>
+Language:
+MIME-Version: 1.0
+Content-Type: text/plain; charset=CHARSET
+Content-Transfer-Encoding: 8bit
+Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;
+ CONTENT
+
+ options = {
+ :msgstr => content,
+ :translator_comment => comment,
+ :flags => ['fuzzy'],
+ }
+ RDoc::Generator::POT::POEntry.new('', options)
+ end
+
+ def sort_entries
+ headers, messages = @entries.values.partition do |entry|
+ entry.msgid.empty?
+ end
+ # TODO: sort by location
+ sorted_messages = messages.sort_by do |entry|
+ entry.msgid
+ end
+ headers + sorted_messages
+ end
+
+end
diff --git a/lib/rdoc/generator/pot/po_entry.rb b/lib/rdoc/generator/pot/po_entry.rb
new file mode 100644
index 0000000000..3c278826f4
--- /dev/null
+++ b/lib/rdoc/generator/pot/po_entry.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+##
+# A PO entry in PO
+
+class RDoc::Generator::POT::POEntry
+
+ # The msgid content
+ attr_reader :msgid
+
+ # The msgstr content
+ attr_reader :msgstr
+
+ # The comment content created by translator (PO editor)
+ attr_reader :translator_comment
+
+ # The comment content extracted from source file
+ attr_reader :extracted_comment
+
+ # The locations where the PO entry is extracted
+ attr_reader :references
+
+ # The flags of the PO entry
+ attr_reader :flags
+
+ ##
+ # Creates a PO entry for +msgid+. Other valus can be specified by
+ # +options+.
+
+ def initialize msgid, options = {}
+ @msgid = msgid
+ @msgstr = options[:msgstr] || ""
+ @translator_comment = options[:translator_comment]
+ @extracted_comment = options[:extracted_comment]
+ @references = options[:references] || []
+ @flags = options[:flags] || []
+ end
+
+ ##
+ # Returns the PO entry in PO format.
+
+ def to_s
+ entry = ''
+ entry += format_translator_comment
+ entry += format_extracted_comment
+ entry += format_references
+ entry += format_flags
+ entry += <<-ENTRY
+msgid #{format_message(@msgid)}
+msgstr #{format_message(@msgstr)}
+ ENTRY
+ end
+
+ ##
+ # Merges the PO entry with +other_entry+.
+
+ def merge other_entry
+ options = {
+ :extracted_comment => merge_string(@extracted_comment,
+ other_entry.extracted_comment),
+ :translator_comment => merge_string(@translator_comment,
+ other_entry.translator_comment),
+ :references => merge_array(@references,
+ other_entry.references),
+ :flags => merge_array(@flags,
+ other_entry.flags),
+ }
+ self.class.new(@msgid, options)
+ end
+
+ private
+
+ def format_comment mark, comment
+ return '' unless comment
+ return '' if comment.empty?
+
+ formatted_comment = ''
+ comment.each_line do |line|
+ formatted_comment += "#{mark} #{line}"
+ end
+ formatted_comment += "\n" unless formatted_comment.end_with?("\n")
+ formatted_comment
+ end
+
+ def format_translator_comment
+ format_comment('#', @translator_comment)
+ end
+
+ def format_extracted_comment
+ format_comment('#.', @extracted_comment)
+ end
+
+ def format_references
+ return '' if @references.empty?
+
+ formatted_references = ''
+ @references.sort.each do |file, line|
+ formatted_references += "\#: #{file}:#{line}\n"
+ end
+ formatted_references
+ end
+
+ def format_flags
+ return '' if @flags.empty?
+
+ formatted_flags = flags.join(",")
+ "\#, #{formatted_flags}\n"
+ end
+
+ def format_message message
+ return "\"#{escape(message)}\"" unless message.include?("\n")
+
+ formatted_message = '""'
+ message.each_line do |line|
+ formatted_message += "\n"
+ formatted_message += "\"#{escape(line)}\""
+ end
+ formatted_message
+ end
+
+ def escape string
+ string.gsub(/["\\\t\n]/) do |special_character|
+ case special_character
+ when "\t"
+ "\\t"
+ when "\n"
+ "\\n"
+ else
+ "\\#{special_character}"
+ end
+ end
+ end
+
+ def merge_string string1, string2
+ [string1, string2].compact.join("\n")
+ end
+
+ def merge_array array1, array2
+ (array1 + array2).uniq
+ end
+
+end
diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb
new file mode 100644
index 0000000000..0eef1d03f5
--- /dev/null
+++ b/lib/rdoc/generator/ri.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+##
+# Generates ri data files
+
+class RDoc::Generator::RI
+
+ RDoc::RDoc.add_generator self
+
+ ##
+ # Description of this generator
+
+ DESCRIPTION = 'creates ri data files'
+
+ ##
+ # Set up a new ri generator
+
+ def initialize store, options #:not-new:
+ @options = options
+ @store = store
+ @store.path = '.'
+ end
+
+ ##
+ # Writes the parsed data store to disk for use by ri.
+
+ def generate
+ @store.save
+ end
+
+end
+
diff --git a/lib/rdoc/generator/template/darkfish/.document b/lib/rdoc/generator/template/darkfish/.document
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/.document
diff --git a/lib/rdoc/generator/template/darkfish/_footer.rhtml b/lib/rdoc/generator/template/darkfish/_footer.rhtml
new file mode 100644
index 0000000000..9791b42901
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_footer.rhtml
@@ -0,0 +1,5 @@
+<footer id="validator-badges" role="contentinfo">
+ <p><a href="https://validator.w3.org/check/referer">Validate</a>
+ <p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> <%= RDoc::VERSION %>.
+ <p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
+</footer>
diff --git a/lib/rdoc/generator/template/darkfish/_head.rhtml b/lib/rdoc/generator/template/darkfish/_head.rhtml
new file mode 100644
index 0000000000..8304310d4b
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_head.rhtml
@@ -0,0 +1,23 @@
+<meta charset="<%= @options.charset %>">
+
+<title><%= h @title %></title>
+
+<script type="text/javascript">
+ var rdoc_rel_prefix = "<%= asset_rel_prefix %>/";
+ var index_rel_prefix = "<%= rel_prefix %>/";
+</script>
+
+<script src="<%= asset_rel_prefix %>/js/navigation.js" defer></script>
+<script src="<%= asset_rel_prefix %>/js/search.js" defer></script>
+<script src="<%= asset_rel_prefix %>/js/search_index.js" defer></script>
+<script src="<%= asset_rel_prefix %>/js/searcher.js" defer></script>
+<script src="<%= asset_rel_prefix %>/js/darkfish.js" defer></script>
+
+<link href="<%= asset_rel_prefix %>/css/fonts.css" rel="stylesheet">
+<link href="<%= asset_rel_prefix %>/css/rdoc.css" rel="stylesheet">
+<% if @options.template_stylesheets.flatten.any? then %>
+<% @options.template_stylesheets.flatten.each do |stylesheet| %>
+<link href="<%= asset_rel_prefix %>/<%= File.basename stylesheet %>" rel="stylesheet">
+<% end %>
+<% end %>
+
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml
new file mode 100644
index 0000000000..e889f8063d
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml
@@ -0,0 +1,19 @@
+<% if !svninfo.empty? then %>
+<div id="file-svninfo-section" class="nav-section">
+ <h3>VCS Info</h3>
+
+ <div class="section-body">
+ <dl class="svninfo">
+ <dt>Rev
+ <dd><%= svninfo[:rev] %>
+
+ <dt>Last Checked In
+ <dd><%= svninfo[:commitdate].strftime('%Y-%m-%d %H:%M:%S') %>
+ (<%= svninfo[:commitdelta] %> ago)
+
+ <dt>Checked in by
+ <dd><%= svninfo[:committer] %>
+ </dl>
+ </div>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml
new file mode 100644
index 0000000000..fe54d8339f
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml
@@ -0,0 +1,9 @@
+<div id="classindex-section" class="nav-section">
+ <h3>Class and Module Index</h3>
+
+ <ul class="link-list">
+ <% @modsort.each do |index_klass| %>
+ <li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a>
+ <% end %>
+ </ul>
+</div>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml
new file mode 100644
index 0000000000..2bd8efee99
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml
@@ -0,0 +1,15 @@
+<% unless klass.extends.empty? then %>
+<div id="extends-section" class="nav-section">
+ <h3>Extended With Modules</h3>
+
+ <ul class="link-list">
+ <% klass.each_extend do |ext| %>
+ <% unless String === ext.module then %>
+ <li><a class="extend" href="<%= klass.aref_to ext.module.path %>"><%= ext.module.full_name %></a>
+ <% else %>
+ <li><span class="extend"><%= ext.name %></span>
+ <% end %>
+ <% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml
new file mode 100644
index 0000000000..0ba1d2be80
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml
@@ -0,0 +1,9 @@
+<div id="file-list-section" class="nav-section">
+ <h3>Defined In</h3>
+
+ <ul>
+<% klass.in_files.each do |tl| %>
+ <li><%= h tl.relative_name %>
+<% end %>
+ </ul>
+</div>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml
new file mode 100644
index 0000000000..d141098ecd
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml
@@ -0,0 +1,15 @@
+<% unless klass.includes.empty? then %>
+<div id="includes-section" class="nav-section">
+ <h3>Included Modules</h3>
+
+ <ul class="link-list">
+ <% klass.each_include do |inc| %>
+ <% unless String === inc.module then %>
+ <li><a class="include" href="<%= klass.aref_to inc.module.path %>"><%= inc.module.full_name %></a>
+ <% else %>
+ <li><span class="include"><%= inc.name %></span>
+ <% end %>
+ <% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml
new file mode 100644
index 0000000000..1285bfd732
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml
@@ -0,0 +1,15 @@
+<div id="home-section" class="nav-section">
+ <h3>Documentation</h3>
+
+ <ul>
+ <% installed.each do |name, href, exists, type, _| %>
+ <% next if type == :extra %>
+ <li class="folder">
+ <% if exists then %>
+ <a href="<%= href %>"><%= h name %></a>
+ <% else %>
+ <%= h name %>
+ <% end %>
+ <% end %>
+ </ul>
+</div>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml
new file mode 100644
index 0000000000..45df08d8fe
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml
@@ -0,0 +1,12 @@
+<% unless klass.method_list.empty? then %>
+<!-- Method Quickref -->
+<div id="method-list-section" class="nav-section">
+ <h3>Methods</h3>
+
+ <ul class="link-list" role="directory">
+ <% klass.each_method do |meth| %>
+ <li <% if meth.calls_super %>class="calls-super" <% end %>><a href="#<%= meth.aref %>"><%= meth.singleton ? '::' : '#' %><%= h meth.name %></a>
+ <% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml
new file mode 100644
index 0000000000..d7f330840a
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml
@@ -0,0 +1,11 @@
+<div id="home-section" role="region" title="Quick navigation" class="nav-section">
+ <h2>
+ <a href="<%= rel_prefix %>/index.html" rel="home">Home</a>
+ </h2>
+
+ <div id="table-of-contents-navigation">
+ <a href="<%= rel_prefix %>/table_of_contents.html#pages">Pages</a>
+ <a href="<%= rel_prefix %>/table_of_contents.html#classes">Classes</a>
+ <a href="<%= rel_prefix %>/table_of_contents.html#methods">Methods</a>
+ </div>
+</div>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
new file mode 100644
index 0000000000..5f39825f08
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml
@@ -0,0 +1,12 @@
+<% simple_files = @files.select { |f| f.text? } %>
+<% unless simple_files.empty? then %>
+<div id="fileindex-section" class="nav-section">
+ <h3>Pages</h3>
+
+ <ul class="link-list">
+ <% simple_files.each do |f| %>
+ <li><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
+ <% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml
new file mode 100644
index 0000000000..cc04852652
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml
@@ -0,0 +1,11 @@
+<% if klass.type == 'class' then %>
+<div id="parent-class-section" class="nav-section">
+ <h3>Parent</h3>
+
+ <% if klass.superclass and not String === klass.superclass then %>
+ <p class="link"><a href="<%= klass.aref_to klass.superclass.path %>"><%= klass.superclass.full_name %></a>
+ <% else %>
+ <p class="link"><%= klass.superclass %>
+ <% end %>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml
new file mode 100644
index 0000000000..9c49b31376
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml
@@ -0,0 +1,14 @@
+<div id="search-section" role="search" class="project-section initially-hidden">
+ <form action="#" method="get" accept-charset="utf-8">
+ <div id="search-field-wrapper">
+ <input id="search-field" role="combobox" aria-label="Search"
+ aria-autocomplete="list" aria-controls="search-results"
+ type="text" name="search" placeholder="Search" spellcheck="false"
+ title="Type to search, Up and Down to navigate, Enter to load">
+ </div>
+
+ <ul id="search-results" aria-label="Search Results"
+ aria-busy="false" aria-expanded="false"
+ aria-atomic="false" class="initially-hidden"></ul>
+ </form>
+</div>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml
new file mode 100644
index 0000000000..15ff78ba91
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml
@@ -0,0 +1,11 @@
+<% unless klass.sections.length == 1 then %>
+<div id="sections-section" class="nav-section">
+ <h3>Sections</h3>
+
+ <ul class="link-list" role="directory">
+ <% klass.sort_sections.each do |section| %>
+ <li><a href="#<%= section.aref %>"><%= h section.title %></a></li>
+ <% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
new file mode 100644
index 0000000000..b58e6b3c61
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml
@@ -0,0 +1,18 @@
+<% comment = if current.respond_to? :comment_location then
+ current.comment_location
+ else
+ current.comment
+ end
+ table = current.parse(comment).table_of_contents
+
+ if table.length > 1 then %>
+<div class="nav-section">
+ <h3>Table of Contents</h3>
+
+ <ul class="link-list" role="directory">
+<% table.each do |heading| %>
+ <li><a href="#<%= heading.label current %>"><%= heading.plain_html %></a>
+<% end %>
+ </ul>
+</div>
+<% end %>
diff --git a/lib/rdoc/generator/template/darkfish/class.rhtml b/lib/rdoc/generator/template/darkfish/class.rhtml
new file mode 100644
index 0000000000..7733095086
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/class.rhtml
@@ -0,0 +1,172 @@
+<body id="top" role="document" class="<%= klass.type %>">
+<nav role="navigation">
+ <div id="project-navigation">
+ <%= render '_sidebar_navigation.rhtml' %>
+ <%= render '_sidebar_search.rhtml' %>
+ </div>
+
+ <%= render '_sidebar_table_of_contents.rhtml' %>
+
+ <div id="class-metadata">
+ <%= render '_sidebar_sections.rhtml' %>
+ <%= render '_sidebar_parent.rhtml' %>
+ <%= render '_sidebar_includes.rhtml' %>
+ <%= render '_sidebar_extends.rhtml' %>
+ <%= render '_sidebar_methods.rhtml' %>
+ </div>
+</nav>
+
+<main role="main" aria-labelledby="<%=h klass.aref %>">
+ <h1 id="<%=h klass.aref %>" class="<%= klass.type %>">
+ <%= klass.type %> <%= klass.full_name %>
+ </h1>
+
+ <section class="description">
+ <%= klass.description %>
+ </section>
+
+ <% klass.each_section do |section, constants, attributes| %>
+ <section id="<%= section.aref %>" class="documentation-section">
+ <% if section.title then %>
+ <header class="documentation-section-title">
+ <h2>
+ <%= section.title %>
+ </h2>
+ <span class="section-click-top">
+ <a href="#top">&uarr; top</a>
+ </span>
+ </header>
+ <% end %>
+
+ <% if section.comment then %>
+ <div>
+ <%= section.description %>
+ </div>
+ <% end %>
+
+ <% unless constants.empty? then %>
+ <section class="constants-list">
+ <header>
+ <h3>Constants</h3>
+ </header>
+ <dl>
+ <% constants.each do |const| %>
+ <dt id="<%= const.name %>"><%= const.name %>
+ <% if const.comment then %>
+ <dd><%= const.description.strip %>
+ <% else %>
+ <dd class="missing-docs">(Not documented)
+ <% end %>
+ <% end %>
+ </dl>
+ </section>
+ <% end %>
+
+ <% unless attributes.empty? then %>
+ <section class="attribute-method-details" class="method-section">
+ <header>
+ <h3>Attributes</h3>
+ </header>
+
+ <% attributes.each do |attrib| %>
+ <div id="<%= attrib.aref %>" class="method-detail">
+ <div class="method-heading attribute-method-heading">
+ <span class="method-name"><%= h attrib.name %></span><span
+ class="attribute-access-type">[<%= attrib.rw %>]</span>
+ </div>
+
+ <div class="method-description">
+ <% if attrib.comment then %>
+ <%= attrib.description.strip %>
+ <% else %>
+ <p class="missing-docs">(Not documented)
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+ </section>
+ <% end %>
+
+ <% klass.methods_by_type(section).each do |type, visibilities|
+ next if visibilities.empty?
+ visibilities.each do |visibility, methods|
+ next if methods.empty? %>
+ <section id="<%= visibility %>-<%= type %>-<%= section.aref %>-method-details" class="method-section">
+ <header>
+ <h3><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
+ </header>
+
+ <% methods.each do |method| %>
+ <div id="<%= method.aref %>" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
+ <% if method.call_seq then %>
+ <% method.call_seq.strip.split("\n").each_with_index do |call_seq, i| %>
+ <div class="method-heading">
+ <span class="method-callseq">
+ <%= h(call_seq.strip.
+ gsub( /^\w+\./m, '')).
+ gsub(/(.*)[-=]&gt;/, '\1&rarr;') %>
+ </span>
+ <% if i == 0 and method.token_stream then %>
+ <span class="method-click-advice">click to toggle source</span>
+ <% end %>
+ </div>
+ <% end %>
+ <% else %>
+ <div class="method-heading">
+ <span class="method-name"><%= h method.name %></span><span
+ class="method-args"><%= h method.param_seq %></span>
+ <% if method.token_stream then %>
+ <span class="method-click-advice">click to toggle source</span>
+ <% end %>
+ </div>
+ <% end %>
+
+ <div class="method-description">
+ <% if method.comment then %>
+ <%= method.description.strip %>
+ <% else %>
+ <p class="missing-docs">(Not documented)
+ <% end %>
+ <% if method.calls_super then %>
+ <div class="method-calls-super">
+ Calls superclass method
+ <%=
+ method.superclass_method ?
+ method.formatter.link(method.superclass_method.full_name, method.superclass_method.full_name) : nil
+ %>
+ </div>
+ <% end %>
+
+ <% if method.token_stream then %>
+ <div class="method-source-code" id="<%= method.html_name %>-source">
+ <pre><%= method.markup_code %></pre>
+ </div>
+ <% end %>
+ </div>
+
+ <% unless method.aliases.empty? then %>
+ <div class="aliases">
+ Also aliased as: <%= method.aliases.map do |aka|
+ if aka.parent then # HACK lib/rexml/encodings
+ %{<a href="#{klass.aref_to aka.path}">#{h aka.name}</a>}
+ else
+ h aka.name
+ end
+ end.join ", " %>
+ </div>
+ <% end %>
+
+ <% if method.is_alias_for then %>
+ <div class="aliases">
+ Alias for: <a href="<%= klass.aref_to method.is_alias_for.path %>"><%= h method.is_alias_for.name %></a>
+ </div>
+ <% end %>
+ </div>
+
+ <% end %>
+ </section>
+ <% end
+ end %>
+ </section>
+<% end %>
+</main>
diff --git a/lib/rdoc/generator/template/darkfish/css/fonts.css b/lib/rdoc/generator/template/darkfish/css/fonts.css
new file mode 100644
index 0000000000..57302b5183
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/css/fonts.css
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
+ * with Reserved Font Name "Source". All Rights Reserved. Source is a
+ * trademark of Adobe Systems Incorporated in the United States and/or other
+ * countries.
+ *
+ * This Font Software is licensed under the SIL Open Font License, Version
+ * 1.1.
+ *
+ * This license is copied below, and is also available with a FAQ at:
+ * http://scripts.sil.org/OFL
+ */
+
+@font-face {
+ font-family: "Source Code Pro";
+ font-style: normal;
+ font-weight: 400;
+ src: local("Source Code Pro"),
+ local("SourceCodePro-Regular"),
+ url("../fonts/SourceCodePro-Regular.ttf") format("truetype");
+}
+
+@font-face {
+ font-family: "Source Code Pro";
+ font-style: normal;
+ font-weight: 700;
+ src: local("Source Code Pro Bold"),
+ local("SourceCodePro-Bold"),
+ url("../fonts/SourceCodePro-Bold.ttf") format("truetype");
+}
+
+/*
+ * Copyright (c) 2010, Łukasz Dziedzic (dziedzic@typoland.com),
+ * with Reserved Font Name Lato.
+ *
+ * This Font Software is licensed under the SIL Open Font License, Version
+ * 1.1.
+ *
+ * This license is copied below, and is also available with a FAQ at:
+ * http://scripts.sil.org/OFL
+ */
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 300;
+ src: local("Lato Light"),
+ local("Lato-Light"),
+ url("../fonts/Lato-Light.ttf") format("truetype");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: italic;
+ font-weight: 300;
+ src: local("Lato Light Italic"),
+ local("Lato-LightItalic"),
+ url("../fonts/Lato-LightItalic.ttf") format("truetype");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 700;
+ src: local("Lato Regular"),
+ local("Lato-Regular"),
+ url("../fonts/Lato-Regular.ttf") format("truetype");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: italic;
+ font-weight: 700;
+ src: local("Lato Italic"),
+ local("Lato-Italic"),
+ url("../fonts/Lato-RegularItalic.ttf") format("truetype");
+}
+
+/*
+ * -----------------------------------------------------------
+ * SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+ * -----------------------------------------------------------
+ *
+ * PREAMBLE
+ * The goals of the Open Font License (OFL) are to stimulate worldwide
+ * development of collaborative font projects, to support the font creation
+ * efforts of academic and linguistic communities, and to provide a free and
+ * open framework in which fonts may be shared and improved in partnership
+ * with others.
+ *
+ * The OFL allows the licensed fonts to be used, studied, modified and
+ * redistributed freely as long as they are not sold by themselves. The
+ * fonts, including any derivative works, can be bundled, embedded,
+ * redistributed and/or sold with any software provided that any reserved
+ * names are not used by derivative works. The fonts and derivatives,
+ * however, cannot be released under any other type of license. The
+ * requirement for fonts to remain under this license does not apply
+ * to any document created using the fonts or their derivatives.
+ *
+ * DEFINITIONS
+ * "Font Software" refers to the set of files released by the Copyright
+ * Holder(s) under this license and clearly marked as such. This may
+ * include source files, build scripts and documentation.
+ *
+ * "Reserved Font Name" refers to any names specified as such after the
+ * copyright statement(s).
+ *
+ * "Original Version" refers to the collection of Font Software components as
+ * distributed by the Copyright Holder(s).
+ *
+ * "Modified Version" refers to any derivative made by adding to, deleting,
+ * or substituting -- in part or in whole -- any of the components of the
+ * Original Version, by changing formats or by porting the Font Software to a
+ * new environment.
+ *
+ * "Author" refers to any designer, engineer, programmer, technical
+ * writer or other person who contributed to the Font Software.
+ *
+ * PERMISSION & CONDITIONS
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Font Software, to use, study, copy, merge, embed, modify,
+ * redistribute, and sell modified and unmodified copies of the Font
+ * Software, subject to the following conditions:
+ *
+ * 1) Neither the Font Software nor any of its individual components,
+ * in Original or Modified Versions, may be sold by itself.
+ *
+ * 2) Original or Modified Versions of the Font Software may be bundled,
+ * redistributed and/or sold with any software, provided that each copy
+ * contains the above copyright notice and this license. These can be
+ * included either as stand-alone text files, human-readable headers or
+ * in the appropriate machine-readable metadata fields within text or
+ * binary files as long as those fields can be easily viewed by the user.
+ *
+ * 3) No Modified Version of the Font Software may use the Reserved Font
+ * Name(s) unless explicit written permission is granted by the corresponding
+ * Copyright Holder. This restriction only applies to the primary font name as
+ * presented to the users.
+ *
+ * 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ * Software shall not be used to promote, endorse or advertise any
+ * Modified Version, except to acknowledge the contribution(s) of the
+ * Copyright Holder(s) and the Author(s) or with their explicit written
+ * permission.
+ *
+ * 5) The Font Software, modified or unmodified, in part or in whole,
+ * must be distributed entirely under this license, and must not be
+ * distributed under any other license. The requirement for fonts to
+ * remain under this license does not apply to any document created
+ * using the Font Software.
+ *
+ * TERMINATION
+ * This license becomes null and void if any of the above conditions are
+ * not met.
+ *
+ * DISCLAIMER
+ * THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+ * OTHER DEALINGS IN THE FONT SOFTWARE.
+ */
+
diff --git a/lib/rdoc/generator/template/darkfish/css/rdoc.css b/lib/rdoc/generator/template/darkfish/css/rdoc.css
new file mode 100644
index 0000000000..1bdb6e6223
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/css/rdoc.css
@@ -0,0 +1,611 @@
+/*
+ * "Darkfish" Rdoc CSS
+ * $Id: rdoc.css 54 2009-01-27 01:09:48Z deveiant $
+ *
+ * Author: Michael Granger <ged@FaerieMUD.org>
+ *
+ */
+
+/* vim: ft=css et sw=2 ts=2 sts=2 */
+/* Base Green is: #6C8C22 */
+
+.hide { display: none !important; }
+
+* { padding: 0; margin: 0; }
+
+body {
+ background: #fafafa;
+ font-family: Lato, sans-serif;
+ font-weight: 300;
+}
+
+h1 span,
+h2 span,
+h3 span,
+h4 span,
+h5 span,
+h6 span {
+ position: relative;
+
+ display: none;
+ padding-left: 1em;
+ line-height: 0;
+ vertical-align: baseline;
+ font-size: 10px;
+}
+
+h1 span { top: -1.3em; }
+h2 span { top: -1.2em; }
+h3 span { top: -1.0em; }
+h4 span { top: -0.8em; }
+h5 span { top: -0.5em; }
+h6 span { top: -0.5em; }
+
+h1:hover span,
+h2:hover span,
+h3:hover span,
+h4:hover span,
+h5:hover span,
+h6:hover span {
+ display: inline;
+}
+
+h1:target,
+h2:target,
+h3:target,
+h4:target,
+h5:target,
+h6:target {
+ margin-left: -10px;
+ border-left: 10px solid #f1edba;
+}
+
+:link,
+:visited {
+ color: #6C8C22;
+ text-decoration: none;
+}
+
+:link:hover,
+:visited:hover {
+ border-bottom: 1px dotted #6C8C22;
+}
+
+code,
+pre {
+ font-family: "Source Code Pro", Monaco, monospace;
+}
+
+/* @group Generic Classes */
+
+.initially-hidden {
+ display: none;
+}
+
+#search-field {
+ width: 98%;
+ background: white;
+ border: none;
+ height: 1.5em;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ text-align: left;
+}
+#search-field:focus {
+ background: #f1edba;
+}
+#search-field:-moz-placeholder,
+#search-field::-webkit-input-placeholder {
+ font-weight: bold;
+ color: #666;
+}
+
+.missing-docs {
+ font-size: 120%;
+ background: white url(images/wrench_orange.png) no-repeat 4px center;
+ color: #ccc;
+ line-height: 2em;
+ border: 1px solid #d00;
+ opacity: 1;
+ padding-left: 20px;
+ text-indent: 24px;
+ letter-spacing: 3px;
+ font-weight: bold;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+}
+
+.target-section {
+ border: 2px solid #dcce90;
+ border-left-width: 8px;
+ padding: 0 1em;
+ background: #fff3c2;
+}
+
+/* @end */
+
+/* @group Index Page, Standalone file pages */
+.table-of-contents ul {
+ margin: 1em;
+ list-style: none;
+}
+
+.table-of-contents ul ul {
+ margin-top: 0.25em;
+}
+
+.table-of-contents ul :link,
+.table-of-contents ul :visited {
+ font-size: 16px;
+}
+
+.table-of-contents li {
+ margin-bottom: 0.25em;
+}
+
+.table-of-contents li .toc-toggle {
+ width: 16px;
+ height: 16px;
+ background: url(images/add.png) no-repeat;
+}
+
+.table-of-contents li .toc-toggle.open {
+ background: url(images/delete.png) no-repeat;
+}
+
+/* @end */
+
+/* @group Top-Level Structure */
+
+nav {
+ float: left;
+ width: 260px;
+ font-family: Helvetica, sans-serif;
+ font-size: 14px;
+}
+
+main {
+ display: block;
+ margin: 0 2em 5em 260px;
+ padding-left: 20px;
+ min-width: 340px;
+ font-size: 16px;
+}
+
+main h1,
+main h2,
+main h3,
+main h4,
+main h5,
+main h6 {
+ font-family: Helvetica, sans-serif;
+}
+
+.table-of-contents main {
+ margin-left: 2em;
+}
+
+#validator-badges {
+ clear: both;
+ margin: 1em 1em 2em;
+ font-size: smaller;
+}
+
+/* @end */
+
+/* @group navigation */
+nav {
+ margin-bottom: 1em;
+}
+
+nav .nav-section {
+ margin-top: 2em;
+ border-top: 2px solid #aaa;
+ font-size: 90%;
+ overflow: hidden;
+}
+
+nav h2 {
+ margin: 0;
+ padding: 2px 8px 2px 8px;
+ background-color: #e8e8e8;
+ color: #555;
+ font-size: 125%;
+ text-align: center;
+}
+
+nav h3,
+#table-of-contents-navigation {
+ margin: 0;
+ padding: 2px 8px 2px 8px;
+ text-align: right;
+ background-color: #e8e8e8;
+ color: #555;
+}
+
+nav ul,
+nav dl,
+nav p {
+ padding: 4px 8px 0;
+ list-style: none;
+}
+
+#project-navigation .nav-section {
+ margin: 0;
+ border-top: 0;
+}
+
+#home-section h2 {
+ text-align: center;
+}
+
+#table-of-contents-navigation {
+ font-size: 1.2em;
+ font-weight: bold;
+ text-align: center;
+}
+
+#search-section {
+ margin-top: 0;
+ border-top: 0;
+}
+
+#search-field-wrapper {
+ border-top: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+ padding: 3px 8px;
+ background-color: #e8e8e8;
+ color: #555;
+}
+
+ul.link-list li {
+ white-space: nowrap;
+ line-height: 1.4em;
+}
+
+ul.link-list .type {
+ font-size: 8px;
+ text-transform: uppercase;
+ color: white;
+ background: #969696;
+ padding: 2px 4px;
+ -webkit-border-radius: 5px;
+}
+
+.calls-super {
+ background: url(images/arrow_up.png) no-repeat right center;
+}
+
+/* @end */
+
+/* @group Documentation Section */
+main {
+ color: #333;
+}
+
+main > h1:first-child,
+main > h2:first-child,
+main > h3:first-child,
+main > h4:first-child,
+main > h5:first-child,
+main > h6:first-child {
+ margin-top: 0px;
+}
+
+main sup {
+ vertical-align: super;
+ font-size: 0.8em;
+}
+
+/* The heading with the class name */
+main h1[class] {
+ margin-top: 0;
+ margin-bottom: 1em;
+ font-size: 2em;
+ color: #6C8C22;
+}
+
+main h1 {
+ margin: 2em 0 0.5em;
+ font-size: 1.7em;
+}
+
+main h2 {
+ margin: 2em 0 0.5em;
+ font-size: 1.5em;
+}
+
+main h3 {
+ margin: 2em 0 0.5em;
+ font-size: 1.2em;
+}
+
+main h4 {
+ margin: 2em 0 0.5em;
+ font-size: 1.1em;
+}
+
+main h5 {
+ margin: 2em 0 0.5em;
+ font-size: 1em;
+}
+
+main h6 {
+ margin: 2em 0 0.5em;
+ font-size: 1em;
+}
+
+main p {
+ margin: 0 0 0.5em;
+ line-height: 1.4em;
+}
+
+main pre {
+ margin: 1.2em 0.5em;
+ padding: 1em;
+ font-size: 0.8em;
+}
+
+main hr {
+ margin: 1.5em 1em;
+ border: 2px solid #ddd;
+}
+
+main blockquote {
+ margin: 0 2em 1.2em 1.2em;
+ padding-left: 0.5em;
+ border-left: 2px solid #ddd;
+}
+
+main ol,
+main ul {
+ margin: 1em 2em;
+}
+
+main li > p {
+ margin-bottom: 0.5em;
+}
+
+main dl {
+ margin: 1em 0.5em;
+}
+
+main dt {
+ margin-bottom: 0.5em;
+ font-weight: bold;
+}
+
+main dd {
+ margin: 0 1em 1em 0.5em;
+}
+
+main header h2 {
+ margin-top: 2em;
+ border-width: 0;
+ border-top: 4px solid #bbb;
+ font-size: 130%;
+}
+
+main header h3 {
+ margin: 2em 0 1.5em;
+ border-width: 0;
+ border-top: 3px solid #bbb;
+ font-size: 120%;
+}
+
+.documentation-section-title {
+ position: relative;
+}
+.documentation-section-title .section-click-top {
+ position: absolute;
+ top: 6px;
+ left: 12px;
+ font-size: 10px;
+ color: #9b9877;
+ visibility: hidden;
+ padding-left: 0.5px;
+}
+
+.documentation-section-title:hover .section-click-top {
+ visibility: visible;
+}
+
+.constants-list > dl {
+ margin: 1em 0 2em;
+ border: 0;
+}
+
+.constants-list > dl dt {
+ margin-bottom: 0.75em;
+ padding-left: 0;
+ font-family: "Source Code Pro", Monaco, monospace;
+ font-size: 110%;
+}
+
+.constants-list > dl dt a {
+ color: inherit;
+}
+
+.constants-list > dl dd {
+ margin: 0 0 2em 0;
+ padding: 0;
+ color: #666;
+}
+
+.documentation-section h2 {
+ position: relative;
+}
+
+.documentation-section h2 a {
+ position: absolute;
+ top: 8px;
+ right: 10px;
+ font-size: 12px;
+ color: #9b9877;
+ visibility: hidden;
+}
+
+.documentation-section h2:hover a {
+ visibility: visible;
+}
+
+/* @group Method Details */
+
+main .method-source-code {
+ max-height: 0;
+ overflow: hidden;
+ transition-duration: 200ms;
+ transition-delay: 0ms;
+ transition-property: all;
+ transition-timing-function: ease-in-out;
+}
+
+main .method-source-code.active-menu {
+ max-height: 100vh;
+}
+
+main .method-description .method-calls-super {
+ color: #333;
+ font-weight: bold;
+}
+
+main .method-detail {
+ margin-bottom: 2.5em;
+ cursor: pointer;
+}
+
+main .method-detail:target {
+ margin-left: -10px;
+ border-left: 10px solid #f1edba;
+}
+
+main .method-heading {
+ position: relative;
+ font-family: "Source Code Pro", Monaco, monospace;
+ font-size: 110%;
+ font-weight: bold;
+ color: #333;
+}
+main .method-heading :link,
+main .method-heading :visited {
+ color: inherit;
+}
+main .method-click-advice {
+ position: absolute;
+ top: 2px;
+ right: 5px;
+ font-size: 12px;
+ color: #9b9877;
+ visibility: hidden;
+ padding-right: 20px;
+ line-height: 20px;
+ background: url(images/zoom.png) no-repeat right top;
+}
+main .method-heading:hover .method-click-advice {
+ visibility: visible;
+}
+
+main .method-alias .method-heading {
+ color: #666;
+}
+
+main .method-description,
+main .aliases {
+ margin-top: 0.75em;
+ color: #333;
+}
+
+main .aliases {
+ padding-top: 4px;
+ font-style: italic;
+ cursor: default;
+}
+main .method-description ul {
+ margin-left: 1.5em;
+}
+
+main #attribute-method-details .method-detail:hover {
+ background-color: transparent;
+ cursor: default;
+}
+main .attribute-access-type {
+ text-transform: uppercase;
+ padding: 0 1em;
+}
+/* @end */
+
+/* @end */
+
+/* @group Source Code */
+
+pre {
+ margin: 0.5em 0;
+ border: 1px dashed #999;
+ padding: 0.5em;
+ background: #262626;
+ color: white;
+ overflow: auto;
+}
+
+.ruby-constant { color: #7fffd4; background: transparent; }
+.ruby-keyword { color: #00ffff; background: transparent; }
+.ruby-ivar { color: #eedd82; background: transparent; }
+.ruby-operator { color: #00ffee; background: transparent; }
+.ruby-identifier { color: #ffdead; background: transparent; }
+.ruby-node { color: #ffa07a; background: transparent; }
+.ruby-comment { color: #dc0000; background: transparent; }
+.ruby-regexp { color: #ffa07a; background: transparent; }
+.ruby-value { color: #7fffd4; background: transparent; }
+
+/* @end */
+
+
+/* @group search results */
+#search-results {
+ font-family: Lato, sans-serif;
+ font-weight: 300;
+}
+
+#search-results .search-match {
+ font-family: Helvetica, sans-serif;
+ font-weight: normal;
+}
+
+#search-results .search-selected {
+ background: #e8e8e8;
+ border-bottom: 1px solid transparent;
+}
+
+#search-results li {
+ list-style: none;
+ border-bottom: 1px solid #aaa;
+ margin-bottom: 0.5em;
+}
+
+#search-results li:last-child {
+ border-bottom: none;
+ margin-bottom: 0;
+}
+
+#search-results li p {
+ padding: 0;
+ margin: 0.5em;
+}
+
+#search-results .search-namespace {
+ font-weight: bold;
+}
+
+#search-results li em {
+ background: yellow;
+ font-style: normal;
+}
+
+#search-results pre {
+ margin: 0.5em;
+ font-family: "Source Code Pro", Monaco, monospace;
+}
+
+/* @end */
+
diff --git a/lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf b/lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf
new file mode 100644
index 0000000000..b49dd43729
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf b/lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf
new file mode 100644
index 0000000000..7959fef075
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf b/lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf
new file mode 100644
index 0000000000..839cd589dc
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf b/lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf
new file mode 100644
index 0000000000..bababa09e3
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf b/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf
new file mode 100644
index 0000000000..61e3090c1c
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf b/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf
new file mode 100644
index 0000000000..85686d967d
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/add.png b/lib/rdoc/generator/template/darkfish/images/add.png
new file mode 100644
index 0000000000..6332fefea4
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/add.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/arrow_up.png b/lib/rdoc/generator/template/darkfish/images/arrow_up.png
new file mode 100644
index 0000000000..1ebb193243
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/arrow_up.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/brick.png b/lib/rdoc/generator/template/darkfish/images/brick.png
new file mode 100644
index 0000000000..7851cf34c9
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/brick.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/brick_link.png b/lib/rdoc/generator/template/darkfish/images/brick_link.png
new file mode 100644
index 0000000000..9ebf013a23
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/brick_link.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/bug.png b/lib/rdoc/generator/template/darkfish/images/bug.png
new file mode 100644
index 0000000000..2d5fb90ec6
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/bug.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/bullet_black.png b/lib/rdoc/generator/template/darkfish/images/bullet_black.png
new file mode 100644
index 0000000000..57619706d1
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/bullet_black.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png b/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png
new file mode 100644
index 0000000000..b47ce55f68
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png b/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png
new file mode 100644
index 0000000000..9ab4a89664
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/date.png b/lib/rdoc/generator/template/darkfish/images/date.png
new file mode 100644
index 0000000000..783c83357f
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/date.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/delete.png b/lib/rdoc/generator/template/darkfish/images/delete.png
new file mode 100644
index 0000000000..08f249365a
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/delete.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/find.png b/lib/rdoc/generator/template/darkfish/images/find.png
new file mode 100644
index 0000000000..1547479646
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/find.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif b/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif
new file mode 100644
index 0000000000..82290f4833
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png b/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png
new file mode 100644
index 0000000000..c6473b324e
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/package.png b/lib/rdoc/generator/template/darkfish/images/package.png
new file mode 100644
index 0000000000..da3c2a2d74
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/package.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/page_green.png b/lib/rdoc/generator/template/darkfish/images/page_green.png
new file mode 100644
index 0000000000..de8e003f9f
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/page_green.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/page_white_text.png b/lib/rdoc/generator/template/darkfish/images/page_white_text.png
new file mode 100644
index 0000000000..813f712f72
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/page_white_text.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/page_white_width.png b/lib/rdoc/generator/template/darkfish/images/page_white_width.png
new file mode 100644
index 0000000000..1eb880947d
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/page_white_width.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/plugin.png b/lib/rdoc/generator/template/darkfish/images/plugin.png
new file mode 100644
index 0000000000..6187b15aec
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/plugin.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/ruby.png b/lib/rdoc/generator/template/darkfish/images/ruby.png
new file mode 100644
index 0000000000..f763a16880
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/ruby.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/tag_blue.png b/lib/rdoc/generator/template/darkfish/images/tag_blue.png
new file mode 100644
index 0000000000..3f02b5f8f8
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/tag_blue.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/tag_green.png b/lib/rdoc/generator/template/darkfish/images/tag_green.png
new file mode 100644
index 0000000000..83ec984bd7
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/tag_green.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/transparent.png b/lib/rdoc/generator/template/darkfish/images/transparent.png
new file mode 100644
index 0000000000..d665e179ef
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/transparent.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/wrench.png b/lib/rdoc/generator/template/darkfish/images/wrench.png
new file mode 100644
index 0000000000..5c8213fef5
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/wrench.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/wrench_orange.png b/lib/rdoc/generator/template/darkfish/images/wrench_orange.png
new file mode 100644
index 0000000000..565a9330e0
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/wrench_orange.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/images/zoom.png b/lib/rdoc/generator/template/darkfish/images/zoom.png
new file mode 100644
index 0000000000..908612e394
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/images/zoom.png
Binary files differ
diff --git a/lib/rdoc/generator/template/darkfish/index.rhtml b/lib/rdoc/generator/template/darkfish/index.rhtml
new file mode 100644
index 0000000000..7d1c74807b
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/index.rhtml
@@ -0,0 +1,23 @@
+<body id="top" role="document" class="file">
+<nav role="navigation">
+ <div id="project-navigation">
+ <%= render '_sidebar_navigation.rhtml' %>
+
+ <%= render '_sidebar_search.rhtml' %>
+ </div>
+
+ <div id="project-metadata">
+ <%= render '_sidebar_pages.rhtml' %>
+ <%= render '_sidebar_classes.rhtml' %>
+ </div>
+</nav>
+
+<main role="main">
+<% if @options.main_page and
+ main_page = @files.find { |f| f.full_name == @options.main_page } then %>
+<%= main_page.description %>
+<% else %>
+<p>This is the API documentation for <%= @title %>.
+<% end %>
+</main>
+
diff --git a/lib/rdoc/generator/template/darkfish/js/darkfish.js b/lib/rdoc/generator/template/darkfish/js/darkfish.js
new file mode 100644
index 0000000000..111bbf8eb9
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/js/darkfish.js
@@ -0,0 +1,84 @@
+/**
+ *
+ * Darkfish Page Functions
+ * $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $
+ *
+ * Author: Michael Granger <mgranger@laika.com>
+ *
+ */
+
+/* Provide console simulation for firebug-less environments */
+/*
+if (!("console" in window) || !("firebug" in console)) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+ "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+
+ window.console = {};
+ for (var i = 0; i < names.length; ++i)
+ window.console[names[i]] = function() {};
+};
+*/
+
+
+function showSource( e ) {
+ var target = e.target;
+ while (!target.classList.contains('method-detail')) {
+ target = target.parentNode;
+ }
+ if (typeof target !== "undefined" && target !== null) {
+ target = target.querySelector('.method-source-code');
+ }
+ if (typeof target !== "undefined" && target !== null) {
+ target.classList.toggle('active-menu')
+ }
+};
+
+function hookSourceViews() {
+ document.querySelectorAll('.method-heading').forEach(function (codeObject) {
+ codeObject.addEventListener('click', showSource);
+ });
+};
+
+function hookSearch() {
+ var input = document.querySelector('#search-field');
+ var result = document.querySelector('#search-results');
+ result.classList.remove("initially-hidden");
+
+ var search_section = document.querySelector('#search-section');
+ search_section.classList.remove("initially-hidden");
+
+ var search = new Search(search_data, input, result);
+
+ search.renderItem = function(result) {
+ var li = document.createElement('li');
+ var html = '';
+
+ // TODO add relative path to <script> per-page
+ html += '<p class="search-match"><a href="' + index_rel_prefix + result.path + '">' + this.hlt(result.title);
+ if (result.params)
+ html += '<span class="params">' + result.params + '</span>';
+ html += '</a>';
+
+
+ if (result.namespace)
+ html += '<p class="search-namespace">' + this.hlt(result.namespace);
+
+ if (result.snippet)
+ html += '<div class="search-snippet">' + result.snippet + '</div>';
+
+ li.innerHTML = html;
+
+ return li;
+ }
+
+ search.select = function(result) {
+ window.location.href = result.firstChild.firstChild.href;
+ }
+
+ search.scrollIntoView = search.scrollInWindow;
+};
+
+document.addEventListener('DOMContentLoaded', function() {
+ hookSourceViews();
+ hookSearch();
+});
diff --git a/lib/rdoc/generator/template/darkfish/js/search.js b/lib/rdoc/generator/template/darkfish/js/search.js
new file mode 100644
index 0000000000..b558ca5b4f
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/js/search.js
@@ -0,0 +1,110 @@
+Search = function(data, input, result) {
+ this.data = data;
+ this.input = input;
+ this.result = result;
+
+ this.current = null;
+ this.view = this.result.parentNode;
+ this.searcher = new Searcher(data.index);
+ this.init();
+}
+
+Search.prototype = Object.assign({}, Navigation, new function() {
+ var suid = 1;
+
+ this.init = function() {
+ var _this = this;
+ var observer = function(e) {
+ switch(e.keyCode) {
+ case 38: // Event.KEY_UP
+ case 40: // Event.KEY_DOWN
+ return;
+ }
+ _this.search(_this.input.value);
+ };
+ this.input.addEventListener('keyup', observer);
+ this.input.addEventListener('click', observer); // mac's clear field
+
+ this.searcher.ready(function(results, isLast) {
+ _this.addResults(results, isLast);
+ })
+
+ this.initNavigation();
+ this.setNavigationActive(false);
+ }
+
+ this.search = function(value, selectFirstMatch) {
+ value = value.trim().toLowerCase();
+ if (value) {
+ this.setNavigationActive(true);
+ } else {
+ this.setNavigationActive(false);
+ }
+
+ if (value == '') {
+ this.lastQuery = value;
+ this.result.innerHTML = '';
+ this.result.setAttribute('aria-expanded', 'false');
+ this.setNavigationActive(false);
+ } else if (value != this.lastQuery) {
+ this.lastQuery = value;
+ this.result.setAttribute('aria-busy', 'true');
+ this.result.setAttribute('aria-expanded', 'true');
+ this.firstRun = true;
+ this.searcher.find(value);
+ }
+ }
+
+ this.addResults = function(results, isLast) {
+ var target = this.result;
+ if (this.firstRun && (results.length > 0 || isLast)) {
+ this.current = null;
+ this.result.innerHTML = '';
+ }
+
+ for (var i=0, l = results.length; i < l; i++) {
+ var item = this.renderItem.call(this, results[i]);
+ item.setAttribute('id', 'search-result-' + target.childElementCount);
+ target.appendChild(item);
+ };
+
+ if (this.firstRun && results.length > 0) {
+ this.firstRun = false;
+ this.current = target.firstChild;
+ this.current.classList.add('search-selected');
+ }
+ //TODO: ECMAScript
+ //if (jQuery.browser.msie) this.$element[0].className += '';
+
+ if (isLast) this.result.setAttribute('aria-busy', 'false');
+ }
+
+ this.move = function(isDown) {
+ if (!this.current) return;
+ var next = isDown ? this.current.nextElementSibling : this.current.previousElementSibling;
+ if (next) {
+ this.current.classList.remove('search-selected');
+ next.classList.add('search-selected');
+ this.input.setAttribute('aria-activedescendant', next.getAttribute('id'));
+ this.scrollIntoView(next, this.view);
+ this.current = next;
+ this.input.value = next.firstChild.firstChild.text;
+ this.input.select();
+ }
+ return true;
+ }
+
+ this.hlt = function(html) {
+ return this.escapeHTML(html).
+ replace(/\u0001/g, '<em>').
+ replace(/\u0002/g, '</em>');
+ }
+
+ this.escapeHTML = function(html) {
+ return html.replace(/[&<>]/g, function(c) {
+ return '&#' + c.charCodeAt(0) + ';';
+ });
+ }
+
+});
+
diff --git a/lib/rdoc/generator/template/darkfish/page.rhtml b/lib/rdoc/generator/template/darkfish/page.rhtml
new file mode 100644
index 0000000000..4a6b006bb3
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/page.rhtml
@@ -0,0 +1,18 @@
+<body id="top" role="document" class="file">
+<nav role="navigation">
+ <div id="project-navigation">
+ <%= render '_sidebar_navigation.rhtml' %>
+ <%= render '_sidebar_search.rhtml' %>
+ </div>
+
+ <%= render '_sidebar_table_of_contents.rhtml' %>
+
+ <div id="project-metadata">
+ <%= render '_sidebar_pages.rhtml' %>
+ </div>
+</nav>
+
+<main role="main" aria-label="Page <%=h file.full_name%>">
+<%= file.description %>
+</main>
+
diff --git a/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml b/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml
new file mode 100644
index 0000000000..f0841572c3
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml
@@ -0,0 +1,18 @@
+<body role="document">
+<nav role="navigation">
+ <%= render '_sidebar_navigation.rhtml' %>
+
+ <%= render '_sidebar_search.rhtml' %>
+
+ <div id="project-metadata">
+ <%= render '_sidebar_pages.rhtml' %>
+ <%= render '_sidebar_classes.rhtml' %>
+ </div>
+</nav>
+
+<main role="main">
+ <h1>Not Found</h1>
+
+ <p><%= message %>
+</main>
+
diff --git a/lib/rdoc/generator/template/darkfish/servlet_root.rhtml b/lib/rdoc/generator/template/darkfish/servlet_root.rhtml
new file mode 100644
index 0000000000..3a33659aea
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/servlet_root.rhtml
@@ -0,0 +1,63 @@
+<body role="document">
+<nav role="navigation">
+ <div id="project-navigation">
+ <div id="home-section" class="nav-section">
+ <h2>
+ <a href="<%= rel_prefix %>/" rel="home">Home</a>
+ </h2>
+ </div>
+
+ <%= render '_sidebar_search.rhtml' %>
+ </div>
+
+<%= render '_sidebar_installed.rhtml' %>
+</nav>
+
+<main role="main">
+ <h1>Local RDoc Documentation</h1>
+
+ <p>Here you can browse local documentation from the ruby standard library and
+ your installed gems.
+
+<% extra_dirs = installed.select { |_, _, _, type,| type == :extra } %>
+<% unless extra_dirs.empty? %>
+ <h2>Extra Documentation Directories</h2>
+
+ <p>The following additional documentation directories are available:</p>
+
+ <ol>
+ <% extra_dirs.each do |name, href, exists, _, path| %>
+ <li>
+ <% if exists %>
+ <a href="<%= href %>"><%= h name %></a> (<%= h path %>)
+ <% else %>
+ <%= h name %> (<%= h path %>; <i>not available</i>)
+ <% end %>
+ </li>
+ <% end %>
+ </ol>
+<% end %>
+
+<% gems = installed.select { |_, _, _, type,| type == :gem } %>
+<% missing = gems.reject { |_, _, exists,| exists } %>
+<% unless missing.empty? then %>
+ <h2>Missing Gem Documentation</h2>
+
+ <p>You are missing documentation for some of your installed gems.
+ You can install missing documentation for gems by running
+ <kbd>gem rdoc --all</kbd>. After installing the missing documentation you
+ only need to reload this page. The newly created documentation will
+ automatically appear.
+
+ <p>You can also install documentation for a specific gem by running one of
+ the following commands.
+
+ <ul>
+ <% names = missing.map { |name,| name.sub(/-([^-]*)$/, '') }.uniq %>
+ <% names.each do |name| %>
+ <li><kbd>gem rdoc <%=h name %></kbd>
+ <% end %>
+ </ul>
+<% end %>
+</main>
+
diff --git a/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
new file mode 100644
index 0000000000..7ff1a9e93e
--- /dev/null
+++ b/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
@@ -0,0 +1,58 @@
+<body id="top" class="table-of-contents">
+<main role="main">
+<h1 class="class"><%= h @title %></h1>
+
+<% simple_files = @files.select { |f| f.text? } %>
+<% unless simple_files.empty? then %>
+<h2 id="pages">Pages</h2>
+<ul>
+<% simple_files.sort.each do |file| %>
+ <li class="file">
+ <a href="<%= file.path %>"><%= h file.page_name %></a>
+<%
+ # HACK table_of_contents should not exist on Document
+ table = file.parse(file.comment).table_of_contents
+ unless table.empty? then %>
+ <ul>
+<% table.each do |heading| %>
+ <li><a href="<%= file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a>
+<% end %>
+ </ul>
+<% end %>
+ </li>
+ <% end %>
+</ul>
+<% end %>
+
+<h2 id="classes">Classes and Modules</h2>
+<ul>
+<% @modsort.each do |klass| %>
+ <li class="<%= klass.type %>">
+ <a href="<%= klass.path %>"><%= klass.full_name %></a>
+<% table = []
+ table.concat klass.parse(klass.comment_location).table_of_contents
+ table.concat klass.section_contents
+
+ unless table.empty? then %>
+ <ul>
+<% table.each do |item| %>
+ <li><a href="<%= klass.path %>#<%= item.aref %>"><%= item.plain_html %></a>
+<% end %>
+ </ul>
+<% end %>
+ </li>
+<% end %>
+</ul>
+
+<h2 id="methods">Methods</h2>
+<ul>
+<% @store.all_classes_and_modules.map do |mod|
+ mod.method_list
+ end.flatten.sort.each do |method| %>
+ <li class="method">
+ <a href="<%= method.path %>"><%= h method.pretty_name %></a>
+ &mdash;
+ <span class="container"><%= method.parent.full_name %></span>
+<% end %>
+</ul>
+</main>
diff --git a/lib/rdoc/generator/template/json_index/.document b/lib/rdoc/generator/template/json_index/.document
new file mode 100644
index 0000000000..1713b67654
--- /dev/null
+++ b/lib/rdoc/generator/template/json_index/.document
@@ -0,0 +1 @@
+# ignore all files in this directory
diff --git a/lib/rdoc/generator/template/json_index/js/navigation.js b/lib/rdoc/generator/template/json_index/js/navigation.js
new file mode 100644
index 0000000000..4866fff819
--- /dev/null
+++ b/lib/rdoc/generator/template/json_index/js/navigation.js
@@ -0,0 +1,106 @@
+/*
+ * Navigation allows movement using the arrow keys through the search results.
+ *
+ * When using this library you will need to set scrollIntoView to the
+ * appropriate function for your layout. Use scrollInWindow if the container
+ * is not scrollable and scrollInElement if the container is a separate
+ * scrolling region.
+ */
+Navigation = new function() {
+ this.initNavigation = function() {
+ var _this = this;
+
+ document.addEventListener('keydown', function(e) {
+ _this.onkeydown(e);
+ });
+
+ this.navigationActive = true;
+ }
+
+ this.setNavigationActive = function(state) {
+ this.navigationActive = state;
+ }
+
+ this.onkeydown = function(e) {
+ if (!this.navigationActive) return;
+ switch(e.keyCode) {
+ case 37: //Event.KEY_LEFT:
+ if (this.moveLeft()) e.preventDefault();
+ break;
+ case 38: //Event.KEY_UP:
+ if (e.keyCode == 38 || e.ctrlKey) {
+ if (this.moveUp()) e.preventDefault();
+ }
+ break;
+ case 39: //Event.KEY_RIGHT:
+ if (this.moveRight()) e.preventDefault();
+ break;
+ case 40: //Event.KEY_DOWN:
+ if (e.keyCode == 40 || e.ctrlKey) {
+ if (this.moveDown()) e.preventDefault();
+ }
+ break;
+ case 13: //Event.KEY_RETURN:
+ if (this.current)
+ e.preventDefault();
+ this.select(this.current);
+ break;
+ }
+ if (e.ctrlKey && e.shiftKey) this.select(this.current);
+ }
+
+ this.moveRight = function() {
+ }
+
+ this.moveLeft = function() {
+ }
+
+ this.move = function(isDown) {
+ }
+
+ this.moveUp = function() {
+ return this.move(false);
+ }
+
+ this.moveDown = function() {
+ return this.move(true);
+ }
+
+ /*
+ * Scrolls to the given element in the scrollable element view.
+ */
+ this.scrollInElement = function(element, view) {
+ var offset, viewHeight, viewScroll, height;
+ offset = element.offsetTop;
+ height = element.offsetHeight;
+ viewHeight = view.offsetHeight;
+ viewScroll = view.scrollTop;
+
+ if (offset - viewScroll + height > viewHeight) {
+ view.scrollTop = offset - viewHeight + height;
+ }
+ if (offset < viewScroll) {
+ view.scrollTop = offset;
+ }
+ }
+
+ /*
+ * Scrolls to the given element in the window. The second argument is
+ * ignored
+ */
+ this.scrollInWindow = function(element, ignored) {
+ var offset, viewHeight, viewScroll, height;
+ offset = element.offsetTop;
+ height = element.offsetHeight;
+ viewHeight = window.innerHeight;
+ viewScroll = window.scrollY;
+
+ if (offset - viewScroll + height > viewHeight) {
+ window.scrollTo(window.scrollX, offset - viewHeight + height);
+ }
+ if (offset < viewScroll) {
+ window.scrollTo(window.scrollX, offset);
+ }
+ }
+}
+
diff --git a/lib/rdoc/generator/template/json_index/js/searcher.js b/lib/rdoc/generator/template/json_index/js/searcher.js
new file mode 100644
index 0000000000..e200a168b0
--- /dev/null
+++ b/lib/rdoc/generator/template/json_index/js/searcher.js
@@ -0,0 +1,229 @@
+Searcher = function(data) {
+ this.data = data;
+ this.handlers = [];
+}
+
+Searcher.prototype = new function() {
+ // search is performed in chunks of 1000 for non-blocking user input
+ var CHUNK_SIZE = 1000;
+ // do not try to find more than 100 results
+ var MAX_RESULTS = 100;
+ var huid = 1;
+ var suid = 1;
+ var runs = 0;
+
+ this.find = function(query) {
+ var queries = splitQuery(query);
+ var regexps = buildRegexps(queries);
+ var highlighters = buildHilighters(queries);
+ var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
+ var _this = this;
+
+ this.currentSuid = state.n;
+
+ if (!query) return;
+
+ var run = function() {
+ // stop current search thread if new search started
+ if (state.n != _this.currentSuid) return;
+
+ var results =
+ performSearch(_this.data, regexps, queries, highlighters, state);
+ var hasMore = (state.limit > 0 && state.pass < 4);
+
+ triggerResults.call(_this, results, !hasMore);
+ if (hasMore) {
+ setTimeout(run, 2);
+ }
+ runs++;
+ };
+ runs = 0;
+
+ // start search thread
+ run();
+ }
+
+ /* ----- Events ------ */
+ this.ready = function(fn) {
+ fn.huid = huid;
+ this.handlers.push(fn);
+ }
+
+ /* ----- Utilities ------ */
+ function splitQuery(query) {
+ return query.split(/(\s+|::?|\(\)?)/).filter(function(string) {
+ return string.match(/\S/);
+ });
+ }
+
+ function buildRegexps(queries) {
+ return queries.map(function(query) {
+ return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i');
+ });
+ }
+
+ function buildHilighters(queries) {
+ return queries.map(function(query) {
+ return query.split('').map(function(l, i) {
+ return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
+ }).join('');
+ });
+ }
+
+ // function longMatchRegexp(index, longIndex, regexps) {
+ // for (var i = regexps.length - 1; i >= 0; i--){
+ // if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
+ // };
+ // return true;
+ // }
+
+
+ /* ----- Mathchers ------ */
+
+ /*
+ * This record matches if the index starts with queries[0] and the record
+ * matches all of the regexps
+ */
+ function matchPassBeginning(index, longIndex, queries, regexps) {
+ if (index.indexOf(queries[0]) != 0) return false;
+ for (var i=1, l = regexps.length; i < l; i++) {
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+ return false;
+ };
+ return true;
+ }
+
+ /*
+ * This record matches if the longIndex starts with queries[0] and the
+ * longIndex matches all of the regexps
+ */
+ function matchPassLongIndex(index, longIndex, queries, regexps) {
+ if (longIndex.indexOf(queries[0]) != 0) return false;
+ for (var i=1, l = regexps.length; i < l; i++) {
+ if (!longIndex.match(regexps[i]))
+ return false;
+ };
+ return true;
+ }
+
+ /*
+ * This record matches if the index contains queries[0] and the record
+ * matches all of the regexps
+ */
+ function matchPassContains(index, longIndex, queries, regexps) {
+ if (index.indexOf(queries[0]) == -1) return false;
+ for (var i=1, l = regexps.length; i < l; i++) {
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+ return false;
+ };
+ return true;
+ }
+
+ /*
+ * This record matches if regexps[0] matches the index and the record
+ * matches all of the regexps
+ */
+ function matchPassRegexp(index, longIndex, queries, regexps) {
+ if (!index.match(regexps[0])) return false;
+ for (var i=1, l = regexps.length; i < l; i++) {
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
+ return false;
+ };
+ return true;
+ }
+
+
+ /* ----- Highlighters ------ */
+ function highlightRegexp(info, queries, regexps, highlighters) {
+ var result = createResult(info);
+ for (var i=0, l = regexps.length; i < l; i++) {
+ result.title = result.title.replace(regexps[i], highlighters[i]);
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
+ };
+ return result;
+ }
+
+ function hltSubstring(string, pos, length) {
+ return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
+ }
+
+ function highlightQuery(info, queries, regexps, highlighters) {
+ var result = createResult(info);
+ var pos = 0;
+ var lcTitle = result.title.toLowerCase();
+
+ pos = lcTitle.indexOf(queries[0]);
+ if (pos != -1) {
+ result.title = hltSubstring(result.title, pos, queries[0].length);
+ }
+
+ result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
+ for (var i=1, l = regexps.length; i < l; i++) {
+ result.title = result.title.replace(regexps[i], highlighters[i]);
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
+ };
+ return result;
+ }
+
+ function createResult(info) {
+ var result = {};
+ result.title = info[0];
+ result.namespace = info[1];
+ result.path = info[2];
+ result.params = info[3];
+ result.snippet = info[4];
+ result.badge = info[6];
+ return result;
+ }
+
+ /* ----- Searching ------ */
+ function performSearch(data, regexps, queries, highlighters, state) {
+ var searchIndex = data.searchIndex;
+ var longSearchIndex = data.longSearchIndex;
+ var info = data.info;
+ var result = [];
+ var i = state.from;
+ var l = searchIndex.length;
+ var togo = CHUNK_SIZE;
+ var matchFunc, hltFunc;
+
+ while (state.pass < 4 && state.limit > 0 && togo > 0) {
+ if (state.pass == 0) {
+ matchFunc = matchPassBeginning;
+ hltFunc = highlightQuery;
+ } else if (state.pass == 1) {
+ matchFunc = matchPassLongIndex;
+ hltFunc = highlightQuery;
+ } else if (state.pass == 2) {
+ matchFunc = matchPassContains;
+ hltFunc = highlightQuery;
+ } else if (state.pass == 3) {
+ matchFunc = matchPassRegexp;
+ hltFunc = highlightRegexp;
+ }
+
+ for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
+ if (info[i].n == state.n) continue;
+ if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
+ info[i].n = state.n;
+ result.push(hltFunc(info[i], queries, regexps, highlighters));
+ state.limit--;
+ }
+ };
+ if (searchIndex.length <= i) {
+ state.pass++;
+ i = state.from = 0;
+ } else {
+ state.from = i;
+ }
+ }
+ return result;
+ }
+
+ function triggerResults(results, isLast) {
+ this.handlers.forEach(function(fn) {
+ fn.call(this, results, isLast)
+ });
+ }
+}
+
diff --git a/lib/rdoc/generators/chm_generator.rb b/lib/rdoc/generators/chm_generator.rb
deleted file mode 100644
index 51eeda8dd1..0000000000
--- a/lib/rdoc/generators/chm_generator.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-require 'rdoc/generators/html_generator'
-
-module Generators
-
- class CHMGenerator < HTMLGenerator
-
- HHC_PATH = "c:/Program Files/HTML Help Workshop/hhc.exe"
-
- # Standard generator factory
- def CHMGenerator.for(options)
- CHMGenerator.new(options)
- end
-
-
- def initialize(*args)
- super
- @op_name = @options.op_name || "rdoc"
- check_for_html_help_workshop
- end
-
- def check_for_html_help_workshop
- stat = File.stat(HHC_PATH)
- rescue
- $stderr <<
- "\n.chm output generation requires that Microsoft's Html Help\n" <<
- "Workshop is installed. RDoc looks for it in:\n\n " <<
- HHC_PATH <<
- "\n\nYou can download a copy for free from:\n\n" <<
- " http://msdn.microsoft.com/library/default.asp?" <<
- "url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp\n\n"
-
- exit 99
- end
-
- # Generate the html as normal, then wrap it
- # in a help project
- def generate(info)
- super
- @project_name = @op_name + ".hhp"
- create_help_project
- end
-
- # The project contains the project file, a table of contents
- # and an index
- def create_help_project
- create_project_file
- create_contents_and_index
- compile_project
- end
-
- # The project file links together all the various
- # files that go to make up the help.
-
- def create_project_file
- template = TemplatePage.new(RDoc::Page::HPP_FILE)
- values = { "title" => @options.title, "opname" => @op_name }
- files = []
- @files.each do |f|
- files << { "html_file_name" => f.path }
- end
-
- values['all_html_files'] = files
-
- File.open(@project_name, "w") do |f|
- template.write_html_on(f, values)
- end
- end
-
- # The contents is a list of all files and modules.
- # For each we include as sub-entries the list
- # of methods they contain. As we build the contents
- # we also build an index file
-
- def create_contents_and_index
- contents = []
- index = []
-
- (@files+@classes).sort.each do |entry|
- content_entry = { "c_name" => entry.name, "ref" => entry.path }
- index << { "name" => entry.name, "aref" => entry.path }
-
- internals = []
-
- methods = entry.build_method_summary_list(entry.path)
-
- content_entry["methods"] = methods unless methods.empty?
- contents << content_entry
- index.concat methods
- end
-
- values = { "contents" => contents }
- template = TemplatePage.new(RDoc::Page::CONTENTS)
- File.open("contents.hhc", "w") do |f|
- template.write_html_on(f, values)
- end
-
- values = { "index" => index }
- template = TemplatePage.new(RDoc::Page::CHM_INDEX)
- File.open("index.hhk", "w") do |f|
- template.write_html_on(f, values)
- end
- end
-
- # Invoke the windows help compiler to compiler the project
- def compile_project
- system(HHC_PATH, @project_name)
- end
-
- end
-
-
-end
diff --git a/lib/rdoc/generators/html_generator.rb b/lib/rdoc/generators/html_generator.rb
deleted file mode 100644
index 1f9b808e8d..0000000000
--- a/lib/rdoc/generators/html_generator.rb
+++ /dev/null
@@ -1,1509 +0,0 @@
-# We're responsible for generating all the HTML files
-# from the object tree defined in code_objects.rb. We
-# generate:
-#
-# [files] an html file for each input file given. These
-# input files appear as objects of class
-# TopLevel
-#
-# [classes] an html file for each class or module encountered.
-# These classes are not grouped by file: if a file
-# contains four classes, we'll generate an html
-# file for the file itself, and four html files
-# for the individual classes.
-#
-# [indices] we generate three indices for files, classes,
-# and methods. These are displayed in a browser
-# like window with three index panes across the
-# top and the selected description below
-#
-# Method descriptions appear in whatever entity (file, class,
-# or module) that contains them.
-#
-# We generate files in a structure below a specified subdirectory,
-# normally +doc+.
-#
-# opdir
-# |
-# |___ files
-# | |__ per file summaries
-# |
-# |___ classes
-# |__ per class/module descriptions
-#
-# HTML is generated using the Template class.
-#
-
-require 'ftools'
-
-require 'rdoc/options'
-require 'rdoc/template'
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_html'
-require 'cgi'
-
-module Generators
-
- # Name of sub-direcories that hold file and class/module descriptions
-
- FILE_DIR = "files"
- CLASS_DIR = "classes"
- CSS_NAME = "rdoc-style.css"
-
-
- ##
- # Build a hash of all items that can be cross-referenced.
- # This is used when we output required and included names:
- # if the names appear in this hash, we can generate
- # an html cross reference to the appropriate description.
- # We also use this when parsing comment blocks: any decorated
- # words matching an entry in this list are hyperlinked.
-
- class AllReferences
- @@refs = {}
-
- def AllReferences::reset
- @@refs = {}
- end
-
- def AllReferences.add(name, html_class)
- @@refs[name] = html_class
- end
-
- def AllReferences.[](name)
- @@refs[name]
- end
-
- def AllReferences.keys
- @@refs.keys
- end
- 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
-
- class HyperlinkHtml < SM::ToHtml
- # We need to record the html path of our caller so we can generate
- # correct relative paths for any hyperlinks that we find
- def initialize(from_path, context)
- super()
- @from_path = from_path
-
- @parent_name = context.parent_name
- @parent_name += "::" if @parent_name
- @context = context
- end
-
- # We're invoked when any text matches the CROSSREF pattern
- # (defined in MarkUp). If we fine the corresponding reference,
- # generate a hyperlink. If the name we're looking for contains
- # no punctuation, we look for it up the module/class chain. For
- # example, HyperlinkHtml is found, even without the Generators::
- # prefix, because we look for it in module Generators first.
-
- def handle_special_CROSSREF(special)
- name = special.text
- if name[0,1] == '#'
- lookup = name[1..-1]
- name = lookup unless Options.instance.show_hash
- else
- lookup = name
- end
-
- # Find class, module, or method in class or module.
- if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup
- container = $1
- method = $2
- ref = @context.find_symbol(container, method)
- elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup
- container = $1
- method = $2
- ref = @context.find_symbol(container, method)
- else
- ref = @context.find_symbol(lookup)
- end
-
- if ref and ref.document_self
- "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
- else
- name
- end
- end
-
-
- # Generate a hyperlink for url, labeled with text. Handle the
- # special cases for img: and link: described under handle_special_HYPEDLINK
- def gen_url(url, text)
- if url =~ /([A-Za-z]+):(.*)/
- type = $1
- path = $2
- else
- type = "http"
- path = url
- url = "http://#{url}"
- end
-
- if type == "link"
- if path[0,1] == '#' # is this meaningful?
- url = path
- else
- url = HTMLGenerator.gen_url(@from_path, path)
- end
- end
-
- if (type == "http" || type == "link") &&
- url =~ /\.(gif|png|jpg|jpeg|bmp)$/
-
- "<img src=\"#{url}\" />"
- else
- "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
- end
- end
-
- # And we're invoked with a potential external hyperlink mailto:
- # just gets inserted. http: links are checked to see if they
- # reference an image. If so, that image gets inserted using an
- # <img> tag. Otherwise a conventional <a href> is used. We also
- # support a special type of hyperlink, link:, which is a reference
- # to a local file whose path is relative to the --op directory.
-
- def handle_special_HYPERLINK(special)
- url = special.text
- gen_url(url, url)
- end
-
- # HEre's a hypedlink where the label is different to the URL
- # <label>[url]
- #
-
- def handle_special_TIDYLINK(special)
- text = special.text
-# unless text =~ /(\S+)\[(.*?)\]/
- unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
- return text
- end
- label = $1
- url = $2
- gen_url(url, label)
- end
-
- end
-
-
-
- #####################################################################
- #
- # Handle common markup tasks for the various Html classes
- #
-
- module MarkUp
-
- # Convert a string in markup format into HTML. We keep a cached
- # SimpleMarkup 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
-
- # class names, variable names, or instance variables
- @markup.add_special(/(
- \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))? # A::B.meth(**) (for operator in Fortran95)
- | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))? # meth(**) (for operator in Fortran95)
- | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
- | \b([A-Z]\w+(::\w+)*) # A::B..
- | \#\w+[!?=]? # #meth_name
- | \b\w+([_\/\.]+\w+)*[!?=]? # meth_name
- )/x,
- :CROSSREF)
-
- # external hyperlinks
- @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
-
- # and links of the form <text>[<url>]
- @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
-# @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
-
- end
- unless defined? @html_formatter
- @html_formatter = HyperlinkHtml.new(self.path, self)
- end
-
- # Convert leading comment markers to spaces, but only
- # if all non-blank lines have them
-
- if str =~ /^(?>\s*)[^\#]/
- content = str
- else
- content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
- end
-
- res = @markup.convert(content, @html_formatter)
- if remove_para
- res.sub!(/^<p>/, '')
- res.sub!(/<\/p>$/, '')
- end
- res
- end
-
- # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or
- # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it
- # unmodified.
-
- def style_url(path, css_name=nil)
-# $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )"
- css_name ||= CSS_NAME
- if %r{^(https?:/)?/} =~ css_name
- return css_name
- else
- return HTMLGenerator.gen_url(path, css_name)
- end
- end
-
- # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
- # get the file's path sprintfed into them; otherwise they're just catenated
- # together.
-
- def cvs_url(url, full_path)
- if /%s/ =~ url
- return sprintf( url, full_path )
- else
- return url + full_path
- end
- end
- end
-
-
- #####################################################################
- #
- # A Context is built by the parser to represent a container: contexts
- # hold classes, modules, methods, require lists and include lists.
- # ClassModule and TopLevel are the context objects we process here
- #
- class ContextUser
-
- include MarkUp
-
- attr_reader :context
-
- def initialize(context, options)
- @context = context
- @options = options
- end
-
- # convenience method to build a hyperlink
- def href(link, cls, name)
- %{<a href="#{link}" class="#{cls}">#{name}</a>} #"
- end
-
- # return a reference to outselves to be used as an href=
- # the form depends on whether we're all in one file
- # or in multiple files
-
- def as_href(from_path)
- if @options.all_one_file
- "#" + path
- else
- HTMLGenerator.gen_url(from_path, path)
- end
- end
-
- # Create a list of HtmlMethod objects for each method
- # in the corresponding context object. If the @options.show_all
- # variable is set (corresponding to the <tt>--all</tt> option,
- # we include all methods, otherwise just the public ones.
-
- def collect_methods
- list = @context.method_list
- unless @options.show_all
- list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
- end
- @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
- end
-
- # Build a summary list of all the methods in this context
- def build_method_summary_list(path_prefix="")
- collect_methods unless @methods
- meths = @methods.sort
- res = []
- meths.each do |meth|
- res << {
- "name" => CGI.escapeHTML(meth.name),
- "aref" => "#{path_prefix}\##{meth.aref}"
- }
- end
- res
- end
-
-
- # Build a list of aliases for which we couldn't find a
- # corresponding method
- def build_alias_summary_list(section)
- values = []
- @context.aliases.each do |al|
- next unless al.section == section
- res = {
- 'old_name' => al.old_name,
- 'new_name' => al.new_name,
- }
- if al.comment && !al.comment.empty?
- res['desc'] = markup(al.comment, true)
- end
- values << res
- end
- values
- end
-
- # Build a list of constants
- def build_constants_summary_list(section)
- values = []
- @context.constants.each do |co|
- next unless co.section == section
- res = {
- 'name' => co.name,
- 'value' => CGI.escapeHTML(co.value)
- }
- res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
- values << res
- end
- values
- end
-
- def build_requires_list(context)
- potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
- end
-
- def build_include_list(context)
- potentially_referenced_list(context.includes)
- end
-
- # Build a list from an array of <i>Htmlxxx</i> items. Look up each
- # in the AllReferences hash: if we find a corresponding entry,
- # we generate a hyperlink to it, otherwise just output the name.
- # However, some names potentially need massaging. For example,
- # you may require a Ruby file without the .rb extension,
- # but the file names we know about may have it. To deal with
- # this, we pass in a block which performs the massaging,
- # returning an array of alternative names to match
-
- def potentially_referenced_list(array)
- res = []
- array.each do |i|
- ref = AllReferences[i.name]
-# if !ref
-# container = @context.parent
-# while !ref && container
-# name = container.name + "::" + i.name
-# ref = AllReferences[name]
-# container = container.parent
-# end
-# end
-
- ref = @context.find_symbol(i.name)
- ref = ref.viewer if ref
-
- if !ref && block_given?
- possibles = yield(i.name)
- while !ref and !possibles.empty?
- ref = AllReferences[possibles.shift]
- end
- end
- h_name = CGI.escapeHTML(i.name)
- if ref and ref.document_self
- path = url(ref.path)
- res << { "name" => h_name, "aref" => path }
- else
- res << { "name" => h_name }
- end
- end
- res
- end
-
- # Build an array of arrays of method details. The outer array has up
- # to six entries, public, private, and protected for both class
- # methods, the other for instance methods. The inner arrays contain
- # a hash for each method
-
- def build_method_detail_list(section)
- outer = []
-
- methods = @methods.sort
- for singleton in [true, false]
- for vis in [ :public, :protected, :private ]
- res = []
- methods.each do |m|
- if m.section == section and
- m.document_self and
- m.visibility == vis and
- m.singleton == singleton
- row = {}
- if m.call_seq
- row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
- else
- row["name"] = CGI.escapeHTML(m.name)
- row["params"] = m.params
- end
- desc = m.description.strip
- row["m_desc"] = desc unless desc.empty?
- row["aref"] = m.aref
- row["visibility"] = m.visibility.to_s
-
- alias_names = []
- m.aliases.each do |other|
- if other.viewer # won't be if the alias is private
- alias_names << {
- 'name' => other.name,
- 'aref' => other.viewer.as_href(path)
- }
- end
- end
- unless alias_names.empty?
- row["aka"] = alias_names
- end
-
- if @options.inline_source
- code = m.source_code
- row["sourcecode"] = code if code
- else
- code = m.src_url
- if code
- row["codeurl"] = code
- row["imgurl"] = m.img_url
- end
- end
- res << row
- end
- end
- if res.size > 0
- outer << {
- "type" => vis.to_s.capitalize,
- "category" => singleton ? "Class" : "Instance",
- "methods" => res
- }
- end
- end
- end
- outer
- end
-
- # Build the structured list of classes and modules contained
- # in this context.
-
- def build_class_list(level, from, section, infile=nil)
- res = ""
- prefix = "&nbsp;&nbsp;::" * level;
-
- from.modules.sort.each do |mod|
- next unless mod.section == section
- next if infile && !mod.defined_in?(infile)
- if mod.document_self
- res <<
- prefix <<
- "Module " <<
- href(url(mod.viewer.path), "link", mod.full_name) <<
- "<br />\n" <<
- build_class_list(level + 1, mod, section, infile)
- end
- end
-
- from.classes.sort.each do |cls|
- next unless cls.section == section
- next if infile && !cls.defined_in?(infile)
- if cls.document_self
- res <<
- prefix <<
- "Class " <<
- href(url(cls.viewer.path), "link", cls.full_name) <<
- "<br />\n" <<
- build_class_list(level + 1, cls, section, infile)
- end
- end
-
- res
- end
-
- def url(target)
- HTMLGenerator.gen_url(path, target)
- end
-
- def aref_to(target)
- if @options.all_one_file
- "#" + target
- else
- url(target)
- end
- end
-
- def document_self
- @context.document_self
- end
-
- def diagram_reference(diagram)
- res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
- $1 + url($2) + '"'
- }
- res
- end
-
-
- # Find a symbol in ourselves or our parent
- def find_symbol(symbol, method=nil)
- res = @context.find_symbol(symbol, method)
- if res
- res = res.viewer
- end
- res
- end
-
- # create table of contents if we contain sections
-
- def add_table_of_sections
- toc = []
- @context.sections.each do |section|
- if section.title
- toc << {
- 'secname' => section.title,
- 'href' => section.sequence
- }
- end
- end
-
- @values['toc'] = toc unless toc.empty?
- end
-
-
- end
-
- #####################################################################
- #
- # Wrap a ClassModule context
-
- class HtmlClass < ContextUser
-
- attr_reader :path
-
- def initialize(context, html_file, prefix, options)
- super(context, options)
-
- @html_file = html_file
- @is_module = context.is_module?
- @values = {}
-
- context.viewer = self
-
- if options.all_one_file
- @path = context.full_name
- else
- @path = http_url(context.full_name, prefix)
- end
-
- collect_methods
-
- AllReferences.add(name, self)
- end
-
- # return the relative file name to store this class in,
- # which is also its url
- def http_url(full_name, prefix)
- path = full_name.dup
- if path['<<']
- path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
- end
- File.join(prefix, path.split("::")) + ".html"
- end
-
-
- def name
- @context.full_name
- end
-
- def parent_name
- @context.parent.full_name
- end
-
- def index_name
- name
- end
-
- def write_on(f)
- value_hash
- template = TemplatePage.new(RDoc::Page::BODY,
- RDoc::Page::CLASS_PAGE,
- RDoc::Page::METHOD_LIST)
- template.write_html_on(f, @values)
- end
-
- def value_hash
- class_attribute_values
- add_table_of_sections
-
- @values["charset"] = @options.charset
- @values["style_url"] = style_url(path, @options.css)
-
- d = markup(@context.comment)
- @values["description"] = d unless d.empty?
-
- ml = build_method_summary_list
- @values["methods"] = ml unless ml.empty?
-
- il = build_include_list(@context)
- @values["includes"] = il unless il.empty?
-
- @values["sections"] = @context.sections.map do |section|
-
- secdata = {
- "sectitle" => section.title,
- "secsequence" => section.sequence,
- "seccomment" => markup(section.comment)
- }
-
- al = build_alias_summary_list(section)
- secdata["aliases"] = al unless al.empty?
-
- co = build_constants_summary_list(section)
- secdata["constants"] = co unless co.empty?
-
- al = build_attribute_list(section)
- secdata["attributes"] = al unless al.empty?
-
- cl = build_class_list(0, @context, section)
- secdata["classlist"] = cl unless cl.empty?
-
- mdl = build_method_detail_list(section)
- secdata["method_list"] = mdl unless mdl.empty?
-
- secdata
- end
-
- @values
- end
-
- def build_attribute_list(section)
- atts = @context.attributes.sort
- res = []
- atts.each do |att|
- next unless att.section == section
- if att.visibility == :public || att.visibility == :protected || @options.show_all
- entry = {
- "name" => CGI.escapeHTML(att.name),
- "rw" => att.rw,
- "a_desc" => markup(att.comment, true)
- }
- unless att.visibility == :public || att.visibility == :protected
- entry["rw"] << "-"
- end
- res << entry
- end
- end
- res
- end
-
- def class_attribute_values
- h_name = CGI.escapeHTML(name)
-
- @values["classmod"] = @is_module ? "Module" : "Class"
- @values["title"] = "#{@values['classmod']}: #{h_name}"
-
- c = @context
- c = c.parent while c and !c.diagram
- if c && c.diagram
- @values["diagram"] = diagram_reference(c.diagram)
- end
-
- @values["full_name"] = h_name
-
- parent_class = @context.superclass
-
- if parent_class
- @values["parent"] = CGI.escapeHTML(parent_class)
-
- if parent_name
- lookup = parent_name + "::" + parent_class
- else
- lookup = parent_class
- end
-
- parent_url = AllReferences[lookup] || AllReferences[parent_class]
-
- if parent_url and parent_url.document_self
- @values["par_url"] = aref_to(parent_url.path)
- end
- end
-
- files = []
- @context.in_files.each do |f|
- res = {}
- full_path = CGI.escapeHTML(f.file_absolute_name)
-
- res["full_path"] = full_path
- res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
-
- if @options.webcvs
- res["cvsurl"] = cvs_url( @options.webcvs, full_path )
- end
-
- files << res
- end
-
- @values['infiles'] = files
- end
-
- def <=>(other)
- self.name <=> other.name
- end
-
- end
-
- #####################################################################
- #
- # Handles the mapping of a file's information to HTML. In reality,
- # a file corresponds to a +TopLevel+ object, containing modules,
- # classes, and top-level methods. In theory it _could_ contain
- # attributes and aliases, but we ignore these for now.
-
- class HtmlFile < ContextUser
-
- attr_reader :path
- attr_reader :name
-
- def initialize(context, options, file_dir)
- super(context, options)
-
- @values = {}
-
- if options.all_one_file
- @path = filename_to_label
- else
- @path = http_url(file_dir)
- end
-
- @name = @context.file_relative_name
-
- collect_methods
- AllReferences.add(name, self)
- context.viewer = self
- end
-
- def http_url(file_dir)
- File.join(file_dir, @context.file_relative_name.tr('.', '_')) +
- ".html"
- end
-
- def filename_to_label
- @context.file_relative_name.gsub(/%|\/|\?|\#/) {|s| '%' + ("%x" % s[0]) }
- end
-
- def index_name
- name
- end
-
- def parent_name
- nil
- end
-
- def value_hash
- file_attribute_values
- add_table_of_sections
-
- @values["charset"] = @options.charset
- @values["href"] = path
- @values["style_url"] = style_url(path, @options.css)
-
- if @context.comment
- d = markup(@context.comment)
- @values["description"] = d if d.size > 0
- end
-
- ml = build_method_summary_list
- @values["methods"] = ml unless ml.empty?
-
- il = build_include_list(@context)
- @values["includes"] = il unless il.empty?
-
- rl = build_requires_list(@context)
- @values["requires"] = rl unless rl.empty?
-
- if @options.promiscuous
- file_context = nil
- else
- file_context = @context
- end
-
-
- @values["sections"] = @context.sections.map do |section|
-
- secdata = {
- "sectitle" => section.title,
- "secsequence" => section.sequence,
- "seccomment" => markup(section.comment)
- }
-
- cl = build_class_list(0, @context, section, file_context)
- @values["classlist"] = cl unless cl.empty?
-
- mdl = build_method_detail_list(section)
- secdata["method_list"] = mdl unless mdl.empty?
-
- al = build_alias_summary_list(section)
- secdata["aliases"] = al unless al.empty?
-
- co = build_constants_summary_list(section)
- @values["constants"] = co unless co.empty?
-
- secdata
- end
-
- @values
- end
-
- def write_on(f)
- value_hash
- template = TemplatePage.new(RDoc::Page::BODY,
- RDoc::Page::FILE_PAGE,
- RDoc::Page::METHOD_LIST)
- template.write_html_on(f, @values)
- end
-
- def file_attribute_values
- full_path = @context.file_absolute_name
- short_name = File.basename(full_path)
-
- @values["title"] = CGI.escapeHTML("File: #{short_name}")
-
- if @context.diagram
- @values["diagram"] = diagram_reference(@context.diagram)
- end
-
- @values["short_name"] = CGI.escapeHTML(short_name)
- @values["full_path"] = CGI.escapeHTML(full_path)
- @values["dtm_modified"] = @context.file_stat.mtime.to_s
-
- if @options.webcvs
- @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] )
- end
- end
-
- def <=>(other)
- self.name <=> other.name
- end
- end
-
- #####################################################################
-
- class HtmlMethod
- include MarkUp
-
- attr_reader :context
- attr_reader :src_url
- attr_reader :img_url
- attr_reader :source_code
-
- @@seq = "M000000"
-
- @@all_methods = []
-
- def HtmlMethod::reset
- @@all_methods = []
- end
-
- def initialize(context, html_class, options)
- @context = context
- @html_class = html_class
- @options = options
- @@seq = @@seq.succ
- @seq = @@seq
- @@all_methods << self
-
- context.viewer = self
-
- if (ts = @context.token_stream)
- @source_code = markup_code(ts)
- unless @options.inline_source
- @src_url = create_source_code_file(@source_code)
- @img_url = HTMLGenerator.gen_url(path, 'source.png')
- end
- end
-
- AllReferences.add(name, self)
- end
-
- # return a reference to outselves to be used as an href=
- # the form depends on whether we're all in one file
- # or in multiple files
-
- def as_href(from_path)
- if @options.all_one_file
- "#" + path
- else
- HTMLGenerator.gen_url(from_path, path)
- end
- end
-
- def name
- @context.name
- end
-
- def section
- @context.section
- end
-
- def index_name
- "#{@context.name} (#{@html_class.name})"
- end
-
- def parent_name
- if @context.parent.parent
- @context.parent.parent.full_name
- else
- nil
- end
- end
-
- def aref
- @seq
- end
-
- def path
- if @options.all_one_file
- aref
- else
- @html_class.path + "#" + aref
- end
- end
-
- def description
- markup(@context.comment)
- end
-
- def visibility
- @context.visibility
- end
-
- def singleton
- @context.singleton
- end
-
- def call_seq
- cs = @context.call_seq
- if cs
- cs.gsub(/\n/, "<br />\n")
- else
- nil
- end
- end
-
- def params
- # params coming from a call-seq in 'C' will start with the
- # method name
- p = @context.params
- if p !~ /^\w/
- p = @context.params.gsub(/\s*\#.*/, '')
- p = p.tr("\n", " ").squeeze(" ")
- p = "(" + p + ")" unless p[0] == ?(
-
- if (block = @context.block_params)
- # If this method has explicit block parameters, remove any
- # explicit &block
-
- p.sub!(/,?\s*&\w+/, '')
-
- block.gsub!(/\s*\#.*/, '')
- block = block.tr("\n", " ").squeeze(" ")
- if block[0] == ?(
- block.sub!(/^\(/, '').sub!(/\)/, '')
- end
- p << " {|#{block.strip}| ...}"
- end
- end
- CGI.escapeHTML(p)
- end
-
- def create_source_code_file(code_body)
- meth_path = @html_class.path.sub(/\.html$/, '.src')
- File.makedirs(meth_path)
- file_path = File.join(meth_path, @seq) + ".html"
-
- template = TemplatePage.new(RDoc::Page::SRC_PAGE)
- File.open(file_path, "w") do |f|
- values = {
- 'title' => CGI.escapeHTML(index_name),
- 'code' => code_body,
- 'style_url' => style_url(file_path, @options.css),
- 'charset' => @options.charset
- }
- template.write_html_on(f, values)
- end
- HTMLGenerator.gen_url(path, file_path)
- end
-
- def HtmlMethod.all_methods
- @@all_methods
- end
-
- def <=>(other)
- @context <=> other.context
- end
-
- ##
- # Given a sequence of source tokens, mark up the source code
- # to make it look purty.
-
-
- def markup_code(tokens)
- src = ""
- tokens.each do |t|
- next unless t
- # p t.class
-# style = STYLE_MAP[t.class]
- style = case t
- when RubyToken::TkCONSTANT then "ruby-constant"
- when RubyToken::TkKW then "ruby-keyword kw"
- when RubyToken::TkIVAR then "ruby-ivar"
- when RubyToken::TkOp then "ruby-operator"
- when RubyToken::TkId then "ruby-identifier"
- when RubyToken::TkNode then "ruby-node"
- when RubyToken::TkCOMMENT then "ruby-comment cmt"
- when RubyToken::TkREGEXP then "ruby-regexp re"
- when RubyToken::TkSTRING then "ruby-value str"
- when RubyToken::TkVal then "ruby-value"
- else
- nil
- end
-
- text = CGI.escapeHTML(t.text)
-
- if style
- src << "<span class=\"#{style}\">#{text}</span>"
- else
- src << text
- end
- end
-
- add_line_numbers(src) if Options.instance.include_line_numbers
- src
- end
-
- # we rely on the fact that the first line of a source code
- # listing has
- # # File xxxxx, line dddd
-
- def add_line_numbers(src)
- if src =~ /\A.*, line (\d+)/
- first = $1.to_i - 1
- last = first + src.count("\n")
- size = last.to_s.length
- real_fmt = "%#{size}d: "
- fmt = " " * (size+2)
- src.gsub!(/^/) do
- res = sprintf(fmt, first)
- first += 1
- fmt = real_fmt
- res
- end
- end
- end
-
- def document_self
- @context.document_self
- end
-
- def aliases
- @context.aliases
- end
-
- def find_symbol(symbol, method=nil)
- res = @context.parent.find_symbol(symbol, method)
- if res
- res = res.viewer
- end
- res
- end
- end
-
- #####################################################################
-
- class HTMLGenerator
-
- include MarkUp
-
- ##
- # convert a target url to one that is relative to a given
- # path
-
- def HTMLGenerator.gen_url(path, target)
- from = File.dirname(path)
- to, to_file = File.split(target)
-
- from = from.split("/")
- to = to.split("/")
-
- while from.size > 0 and to.size > 0 and from[0] == to[0]
- from.shift
- to.shift
- end
-
- from.fill("..")
- from.concat(to)
- from << to_file
- File.join(*from)
- end
-
- # Generators may need to return specific subclasses depending
- # on the options they are passed. Because of this
- # we create them using a factory
-
- def HTMLGenerator.for(options)
- AllReferences::reset
- HtmlMethod::reset
-
- if options.all_one_file
- HTMLGeneratorInOne.new(options)
- else
- HTMLGenerator.new(options)
- end
- end
-
- class <<self
- protected :new
- end
-
- # Set up a new HTML generator. Basically all we do here is load
- # up the correct output temlate
-
- def initialize(options) #:not-new:
- @options = options
- load_html_template
- end
-
-
- ##
- # Build the initial indices and output objects
- # based on an array of TopLevel objects containing
- # the extracted information.
-
- def generate(toplevels)
- @toplevels = toplevels
- @files = []
- @classes = []
-
- write_style_sheet
- gen_sub_directories()
- build_indices
- generate_html
- end
-
- private
-
- ##
- # Load up the HTML template specified in the options.
- # If the template name contains a slash, use it literally
- #
- def load_html_template
- template = @options.template
- unless template =~ %r{/|\\}
- template = File.join("rdoc/generators/template",
- @options.generator.key, template)
- end
- require template
- extend RDoc::Page
- rescue LoadError
- $stderr.puts "Could not find HTML template '#{template}'"
- exit 99
- end
-
- ##
- # Write out the style sheet used by the main frames
- #
-
- def write_style_sheet
- template = TemplatePage.new(RDoc::Page::STYLE)
- unless @options.css
- File.open(CSS_NAME, "w") do |f|
- values = { "fonts" => RDoc::Page::FONTS }
- template.write_html_on(f, values)
- end
- end
- end
-
- ##
- # See the comments at the top for a description of the
- # directory structure
-
- def gen_sub_directories
- File.makedirs(FILE_DIR, CLASS_DIR)
- rescue
- $stderr.puts $!.message
- exit 1
- end
-
- ##
- # Generate:
- #
- # * a list of HtmlFile objects for each TopLevel object.
- # * a list of HtmlClass objects for each first level
- # class or module in the TopLevel objects
- # * a complete list of all hyperlinkable terms (file,
- # class, module, and method names)
-
- def build_indices
-
- @toplevels.each do |toplevel|
- @files << HtmlFile.new(toplevel, @options, FILE_DIR)
- end
-
- RDoc::TopLevel.all_classes_and_modules.each do |cls|
- build_class_list(cls, @files[0], CLASS_DIR)
- end
- end
-
- def build_class_list(from, html_file, class_dir)
- @classes << HtmlClass.new(from, html_file, class_dir, @options)
- from.each_classmodule do |mod|
- build_class_list(mod, html_file, class_dir)
- end
- end
-
- ##
- # Generate all the HTML
- #
- def generate_html
- # the individual descriptions for files and classes
- gen_into(@files)
- gen_into(@classes)
- # and the index files
- gen_file_index
- gen_class_index
- gen_method_index
- gen_main_index
-
- # this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
- end
-
- def gen_into(list)
- list.each do |item|
- if item.document_self
- op_file = item.path
- File.makedirs(File.dirname(op_file))
- File.open(op_file, "w") { |file| item.write_on(file) }
- end
- end
-
- end
-
- def gen_file_index
- gen_an_index(@files, 'Files',
- RDoc::Page::FILE_INDEX,
- "fr_file_index.html")
- end
-
- def gen_class_index
- gen_an_index(@classes, 'Classes',
- RDoc::Page::CLASS_INDEX,
- "fr_class_index.html")
- end
-
- def gen_method_index
- gen_an_index(HtmlMethod.all_methods, 'Methods',
- RDoc::Page::METHOD_INDEX,
- "fr_method_index.html")
- end
-
-
- def gen_an_index(collection, title, template, filename)
- template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
- res = []
- collection.sort.each do |f|
- if f.document_self
- res << { "href" => f.path, "name" => f.index_name }
- end
- end
-
- values = {
- "entries" => res,
- 'list_title' => CGI.escapeHTML(title),
- 'index_url' => main_url,
- 'charset' => @options.charset,
- 'style_url' => style_url('', @options.css),
- }
-
- File.open(filename, "w") do |f|
- template.write_html_on(f, values)
- end
- end
-
- # The main index page is mostly a template frameset, but includes
- # the initial page. If the <tt>--main</tt> option was given,
- # we use this as our main page, otherwise we use the
- # first file specified on the command line.
-
- def gen_main_index
- template = TemplatePage.new(RDoc::Page::INDEX)
- File.open("index.html", "w") do |f|
- values = {
- "initial_page" => main_url,
- 'title' => CGI.escapeHTML(@options.title),
- 'charset' => @options.charset
- }
- if @options.inline_source
- values['inline_source'] = true
- end
- template.write_html_on(f, values)
- end
- end
-
- # return the url of the main page
- def main_url
- main_page = @options.main_page
- ref = nil
- if main_page
- ref = AllReferences[main_page]
- if ref
- ref = ref.path
- else
- $stderr.puts "Could not find main page #{main_page}"
- end
- end
-
- unless ref
- for file in @files
- if file.document_self
- ref = file.path
- break
- end
- end
- end
-
- unless ref
- $stderr.puts "Couldn't find anything to document"
- $stderr.puts "Perhaps you've used :stopdoc: in all classes"
- exit(1)
- end
-
- ref
- end
-
-
- end
-
-
- ######################################################################
-
-
- class HTMLGeneratorInOne < HTMLGenerator
-
- def initialize(*args)
- super
- end
-
- ##
- # Build the initial indices and output objects
- # based on an array of TopLevel objects containing
- # the extracted information.
-
- def generate(info)
- @toplevels = info
- @files = []
- @classes = []
- @hyperlinks = {}
-
- build_indices
- generate_xml
- end
-
-
- ##
- # Generate:
- #
- # * a list of HtmlFile objects for each TopLevel object.
- # * a list of HtmlClass objects for each first level
- # class or module in the TopLevel objects
- # * a complete list of all hyperlinkable terms (file,
- # class, module, and method names)
-
- def build_indices
-
- @toplevels.each do |toplevel|
- @files << HtmlFile.new(toplevel, @options, FILE_DIR)
- end
-
- RDoc::TopLevel.all_classes_and_modules.each do |cls|
- build_class_list(cls, @files[0], CLASS_DIR)
- end
- end
-
- def build_class_list(from, html_file, class_dir)
- @classes << HtmlClass.new(from, html_file, class_dir, @options)
- from.each_classmodule do |mod|
- build_class_list(mod, html_file, class_dir)
- end
- end
-
- ##
- # Generate all the HTML. For the one-file case, we generate
- # all the information in to one big hash
- #
- def generate_xml
- values = {
- 'charset' => @options.charset,
- 'files' => gen_into(@files),
- 'classes' => gen_into(@classes),
- 'title' => CGI.escapeHTML(@options.title),
- }
-
- # this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
-
- template = TemplatePage.new(RDoc::Page::ONE_PAGE)
-
- if @options.op_name
- opfile = File.open(@options.op_name, "w")
- else
- opfile = $stdout
- end
- template.write_html_on(opfile, values)
- end
-
- def gen_into(list)
- res = []
- list.each do |item|
- res << item.value_hash
- end
- res
- end
-
- def gen_file_index
- gen_an_index(@files, 'Files')
- end
-
- def gen_class_index
- gen_an_index(@classes, 'Classes')
- end
-
- def gen_method_index
- gen_an_index(HtmlMethod.all_methods, 'Methods')
- end
-
-
- def gen_an_index(collection, title)
- res = []
- collection.sort.each do |f|
- if f.document_self
- res << { "href" => f.path, "name" => f.index_name }
- end
- end
-
- return {
- "entries" => res,
- 'list_title' => title,
- 'index_url' => main_url,
- }
- end
-
- end
-end
diff --git a/lib/rdoc/generators/ri_generator.rb b/lib/rdoc/generators/ri_generator.rb
deleted file mode 100644
index c4b4a7e17c..0000000000
--- a/lib/rdoc/generators/ri_generator.rb
+++ /dev/null
@@ -1,268 +0,0 @@
-# We're responsible for generating all the HTML files
-# from the object tree defined in code_objects.rb. We
-# generate:
-#
-# [files] an html file for each input file given. These
-# input files appear as objects of class
-# TopLevel
-#
-# [classes] an html file for each class or module encountered.
-# These classes are not grouped by file: if a file
-# contains four classes, we'll generate an html
-# file for the file itself, and four html files
-# for the individual classes.
-#
-# [indices] we generate three indices for files, classes,
-# and methods. These are displayed in a browser
-# like window with three index panes across the
-# top and the selected description below
-#
-# Method descriptions appear in whatever entity (file, class,
-# or module) that contains them.
-#
-# We generate files in a structure below a specified subdirectory,
-# normally +doc+.
-#
-# opdir
-# |
-# |___ files
-# | |__ per file summaries
-# |
-# |___ classes
-# |__ per class/module descriptions
-#
-# HTML is generated using the Template class.
-#
-
-require 'ftools'
-
-require 'rdoc/options'
-require 'rdoc/template'
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_flow'
-require 'cgi'
-
-require 'rdoc/ri/ri_cache'
-require 'rdoc/ri/ri_reader'
-require 'rdoc/ri/ri_writer'
-require 'rdoc/ri/ri_descriptions'
-
-module Generators
-
-
- class RIGenerator
-
- # Generators may need to return specific subclasses depending
- # on the options they are passed. Because of this
- # we create them using a factory
-
- def RIGenerator.for(options)
- new(options)
- end
-
- class <<self
- protected :new
- end
-
- # Set up a new HTML generator. Basically all we do here is load
- # up the correct output temlate
-
- def initialize(options) #:not-new:
- @options = options
- @ri_writer = RI::RiWriter.new(options.op_dir)
- @markup = SM::SimpleMarkup.new
- @to_flow = SM::ToFlow.new
- end
-
-
- ##
- # Build the initial indices and output objects
- # based on an array of TopLevel objects containing
- # the extracted information.
-
- def generate(toplevels)
- RDoc::TopLevel.all_classes_and_modules.each do |cls|
- process_class(cls)
- end
- end
-
- def process_class(from_class)
- generate_class_info(from_class)
-
- # now recure into this classes constituent classess
- from_class.each_classmodule do |mod|
- process_class(mod)
- end
- end
-
- def generate_class_info(cls)
- if cls === RDoc::NormalModule
- cls_desc = RI::ModuleDescription.new
- else
- cls_desc = 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)
-
- cls_desc.attributes =cls.attributes.sort.map do |a|
- RI::Attribute.new(a.name, a.rw, markup(a.comment))
- end
-
- cls_desc.constants = cls.constants.map do |c|
- RI::Constant.new(c.name, c.value, markup(c.comment))
- end
-
- cls_desc.includes = cls.includes.map do |i|
- RI::IncludedModule.new(i.name)
- end
-
- class_methods, instance_methods = method_list(cls)
-
- cls_desc.class_methods = class_methods.map do |m|
- RI::MethodSummary.new(m.name)
- end
- cls_desc.instance_methods = instance_methods.map do |m|
- RI::MethodSummary.new(m.name)
- end
-
- update_or_replace(cls_desc)
-
- class_methods.each do |m|
- generate_method_info(cls_desc, m)
- end
-
- instance_methods.each do |m|
- generate_method_info(cls_desc, m)
- end
- end
-
-
- def generate_method_info(cls_desc, method)
- meth_desc = RI::MethodDescription.new
- meth_desc.name = method.name
- meth_desc.full_name = cls_desc.full_name
- if method.singleton
- meth_desc.full_name += "::"
- else
- meth_desc.full_name += "#"
- end
- meth_desc.full_name << method.name
-
- meth_desc.comment = markup(method.comment)
- meth_desc.params = params_of(method)
- meth_desc.visibility = method.visibility.to_s
- meth_desc.is_singleton = method.singleton
- meth_desc.block_params = method.block_params
-
- meth_desc.aliases = method.aliases.map do |a|
- RI::AliasName.new(a.name)
- end
-
- @ri_writer.add_method(cls_desc, meth_desc)
- end
-
- private
-
- # return a list of class and instance methods that we'll be
- # documenting
-
- def method_list(cls)
- list = cls.method_list
- unless @options.show_all
- list = list.find_all do |m|
- m.visibility == :public || m.visibility == :protected || m.force_documentation
- end
- end
-
- c = []
- i = []
- list.sort.each do |m|
- if m.singleton
- c << m
- else
- i << m
- end
- end
- return c,i
- end
-
- def params_of(method)
- if method.call_seq
- method.call_seq
- else
- params = method.params || ""
-
- p = params.gsub(/\s*\#.*/, '')
- p = p.tr("\n", " ").squeeze(" ")
- p = "(" + p + ")" unless p[0] == ?(
-
- if (block = method.block_params)
- block.gsub!(/\s*\#.*/, '')
- block = block.tr("\n", " ").squeeze(" ")
- if block[0] == ?(
- block.sub!(/^\(/, '').sub!(/\)/, '')
- end
- p << " {|#{block.strip}| ...}"
- end
- p
- end
- end
-
- def markup(comment)
- return nil if !comment || comment.empty?
-
- # Convert leading comment markers to spaces, but only
- # if all non-blank lines have them
-
- if comment =~ /^(?>\s*)[^\#]/
- content = comment
- else
- content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
- end
- @markup.convert(content, @to_flow)
- end
-
-
- # By default we replace existing classes with the
- # same name. If the --merge option was given, we instead
- # merge this definition into an existing class. We add
- # our methods, aliases, etc to that class, but do not
- # change the class's description.
-
- def update_or_replace(cls_desc)
- old_cls = nil
-
- if @options.merge
- rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
-
- namespace = rdr.top_level_namespace
- namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
- if namespace.empty?
- $stderr.puts "You asked me to merge this source into existing "
- $stderr.puts "documentation. This file references a class or "
- $stderr.puts "module called #{cls_desc.name} which I don't"
- $stderr.puts "have existing documentation for."
- $stderr.puts
- $stderr.puts "Perhaps you need to generate its documentation first"
- exit 1
- else
- old_cls = namespace[0]
- end
- end
-
- if old_cls.nil?
- # no merge: simply overwrite
- @ri_writer.remove_class(cls_desc)
- @ri_writer.add_class(cls_desc)
- else
- # existing class: merge in
- old_desc = rdr.get_class(old_cls)
-
- old_desc.merge_in(cls_desc)
- @ri_writer.add_class(old_desc)
- end
- end
- end
-end
diff --git a/lib/rdoc/generators/template/chm/chm.rb b/lib/rdoc/generators/template/chm/chm.rb
deleted file mode 100644
index 4a89c26520..0000000000
--- a/lib/rdoc/generators/template/chm/chm.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-module RDoc
-module Page
-
-require "rdoc/generators/template/html/html"
-
-# This is a nasty little hack, but hhc doesn't support the <?xml
-# tag, so...
-
-BODY.sub!(/<\?xml.*\?>/, '')
-SRC_PAGE.sub!(/<\?xml.*\?>/, '')
-
-HPP_FILE = %{
-[OPTIONS]
-Auto Index = Yes
-Compatibility=1.1 or later
-Compiled file=%opname%.chm
-Contents file=contents.hhc
-Full-text search=Yes
-Index file=index.hhk
-Language=0x409 English(United States)
-Title=%title%
-
-[FILES]
-START:all_html_files
-%html_file_name%
-END:all_html_files
-}
-
-CONTENTS = %{
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<HTML>
-<HEAD>
-<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
-<!-- Sitemap 1.0 -->
-</HEAD><BODY>
-<OBJECT type="text/site properties">
- <param name="Foreground" value="0x80">
- <param name="Window Styles" value="0x800025">
- <param name="ImageType" value="Folder">
-</OBJECT>
-<UL>
-START:contents
- <LI> <OBJECT type="text/sitemap">
- <param name="Name" value="%c_name%">
- <param name="Local" value="%ref%">
- </OBJECT>
-IF:methods
-<ul>
-START:methods
- <LI> <OBJECT type="text/sitemap">
- <param name="Name" value="%name%">
- <param name="Local" value="%aref%">
- </OBJECT>
-END:methods
-</ul>
-ENDIF:methods
- </LI>
-END:contents
-</UL>
-</BODY></HTML>
-}
-
-
-CHM_INDEX = %{
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<HTML>
-<HEAD>
-<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
-<!-- Sitemap 1.0 -->
-</HEAD><BODY>
-<OBJECT type="text/site properties">
- <param name="Foreground" value="0x80">
- <param name="Window Styles" value="0x800025">
- <param name="ImageType" value="Folder">
-</OBJECT>
-<UL>
-START:index
- <LI> <OBJECT type="text/sitemap">
- <param name="Name" value="%name%">
- <param name="Local" value="%aref%">
- </OBJECT>
-END:index
-</UL>
-</BODY></HTML>
-}
-end
-end
diff --git a/lib/rdoc/generators/template/html/hefss.rb b/lib/rdoc/generators/template/html/hefss.rb
deleted file mode 100644
index e68ca85823..0000000000
--- a/lib/rdoc/generators/template/html/hefss.rb
+++ /dev/null
@@ -1,418 +0,0 @@
-module RDoc
-module Page
-
-
-FONTS = "Verdana, Arial, Helvetica, sans-serif"
-
-STYLE = %{
-body,p { font-family: Verdana, Arial, Helvetica, sans-serif;
- color: #000040; background: #BBBBBB;
-}
-
-td { font-family: Verdana, Arial, Helvetica, sans-serif;
- color: #000040;
-}
-
-.attr-rw { font-size: small; color: #444488 }
-
-.title-row {color: #eeeeff;
- background: #BBBBDD;
-}
-
-.big-title-font { color: white;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: large;
- height: 50px}
-
-.small-title-font { color: purple;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: small; }
-
-.aqua { color: purple }
-
-.method-name, attr-name {
- font-family: monospace; font-weight: bold;
-}
-
-.tablesubtitle {
- width: 100%;
- margin-top: 1ex;
- margin-bottom: .5ex;
- padding: 5px 0px 5px 20px;
- font-size: large;
- color: purple;
- background: #BBBBCC;
-}
-
-.tablesubsubtitle {
- width: 100%;
- margin-top: 1ex;
- margin-bottom: .5ex;
- padding: 5px 0px 5px 20px;
- font-size: medium;
- color: white;
- background: #BBBBCC;
-}
-
-.name-list {
- font-family: monospace;
- margin-left: 40px;
- margin-bottom: 2ex;
- line-height: 140%;
-}
-
-.description {
- margin-left: 40px;
- margin-bottom: 2ex;
- line-height: 140%;
-}
-
-.methodtitle {
- font-size: medium;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
- color: #0000AA;
-}
-
-.column-title {
- font-size: medium;
- font-weight: bold;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
- color: #3333CC;
- }
-
-.variable-name {
- font-family: monospace;
- font-size: medium;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
- color: #0000AA;
-}
-
-.row-name {
- font-size: medium;
- font-weight: medium;
- font-family: monospace;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
-}
-
-.paramsig {
- font-size: small;
-}
-
-.srcbut { float: right }
-
-}
-
-
-############################################################################
-
-
-BODY = %{
-<html><head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
- <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
- <script type="text/javascript" language="JavaScript">
- <!--
- function popCode(url) {
- parent.frames.source.location = url
- }
- //-->
- </script>
-</head>
-<body bgcolor="#BBBBBB">
-
-!INCLUDE! <!-- banner header -->
-
-IF:diagram
-<table width="100%"><tr><td align="center">
-%diagram%
-</td></tr></table>
-ENDIF:diagram
-
-IF:description
-<div class="description">%description%</div>
-ENDIF:description
-
-IF:requires
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Required files</td></tr>
-</table><br />
-<div class="name-list">
-START:requires
-HREF:aref:name:
-END:requires
-ENDIF:requires
-</div>
-
-IF:methods
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Subroutines and Functions</td></tr>
-</table><br />
-<div class="name-list">
-START:methods
-HREF:aref:name:,
-END:methods
-</div>
-ENDIF:methods
-
-IF:attributes
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Arguments</td></tr>
-</table><br />
-<table cellspacing="5">
-START:attributes
- <tr valign="top">
-IF:rw
- <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
-ENDIF:rw
-IFNOT:rw
- <td></td>
-ENDIF:rw
- <td class="attr-name">%name%</td>
- <td>%a_desc%</td>
- </tr>
-END:attributes
-</table>
-ENDIF:attributes
-
-IF:classlist
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Modules</td></tr>
-</table><br />
-%classlist%<br />
-ENDIF:classlist
-
- !INCLUDE! <!-- method descriptions -->
-
-</body>
-</html>
-}
-
-###############################################################################
-
-FILE_PAGE = <<_FILE_PAGE_
-<table width="100%">
- <tr class="title-row">
- <td><table width="100%"><tr>
- <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font>%short_name%</td>
- <td align="right"><table cellspacing="0" cellpadding="2">
- <tr>
- <td class="small-title-font">Path:</td>
- <td class="small-title-font">%full_path%
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
- </td>
- </tr>
- <tr>
- <td class="small-title-font">Modified:</td>
- <td class="small-title-font">%dtm_modified%</td>
- </tr>
- </table>
- </td></tr></table></td>
- </tr>
-</table><br />
-_FILE_PAGE_
-
-###################################################################
-
-CLASS_PAGE = %{
-<table width="100%" border="0" cellspacing="0">
- <tr class="title-row">
- <td class="big-title-font">
- <font size="-3"><b>%classmod%</b><br /></font>%full_name%
- </td>
- <td align="right">
- <table cellspacing="0" cellpadding="2">
- <tr valign="top">
- <td class="small-title-font">In:</td>
- <td class="small-title-font">
-START:infiles
-HREF:full_path_url:full_path:
-IF:cvsurl
-&nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
-END:infiles
- </td>
- </tr>
-IF:parent
- <tr>
- <td class="small-title-font">Parent:</td>
- <td class="small-title-font">
-IF:par_url
- <a href="%par_url%" class="cyan">
-ENDIF:par_url
-%parent%
-IF:par_url
- </a>
-ENDIF:par_url
- </td>
- </tr>
-ENDIF:parent
- </table>
- </td>
- </tr>
-</table><br />
-}
-
-###################################################################
-
-METHOD_LIST = %{
-IF:includes
-<div class="tablesubsubtitle">Uses</div><br />
-<div class="name-list">
-START:includes
- <span class="method-name">HREF:aref:name:</span>
-END:includes
-</div>
-ENDIF:includes
-
-IF:method_list
-START:method_list
-IF:methods
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
-</table>
-START:methods
-<table width="100%" cellspacing="0" cellpadding="5" border="0">
-<tr><td class="methodtitle">
-<a name="%aref%">
-<b>%name%</b>%params%
-IF:codeurl
-<a href="%codeurl%" target="source" class="srclink">src</a>
-ENDIF:codeurl
-</a></td></tr>
-</table>
-IF:m_desc
-<div class="description">
-%m_desc%
-</div>
-ENDIF:m_desc
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
-}
-
-=begin
-=end
-
-########################## Source code ##########################
-
-SRC_PAGE = %{
-<html>
-<head><title>%title%</title>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<style type="text/css">
- .kw { color: #3333FF; font-weight: bold }
- .cmt { color: green; font-style: italic }
- .str { color: #662222; font-style: italic }
- .re { color: #662222; }
-.ruby-comment { color: green; font-style: italic }
-.ruby-constant { color: #4433aa; font-weight: bold; }
-.ruby-identifier { color: #222222; }
-.ruby-ivar { color: #2233dd; }
-.ruby-keyword { color: #3333FF; font-weight: bold }
-.ruby-node { color: #777777; }
-.ruby-operator { color: #111111; }
-.ruby-regexp { color: #662222; }
-.ruby-value { color: #662222; font-style: italic }
-</style>
-</head>
-<body bgcolor="#BBBBBB">
-<pre>%code%</pre>
-</body>
-</html>
-}
-
-########################## Index ################################
-
-FR_INDEX_BODY = %{
-!INCLUDE!
-}
-
-FILE_INDEX = %{
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<style type="text/css">
-<!--
- body {
-background-color: #bbbbbb;
- font-family: #{FONTS};
- font-size: 11px;
- font-style: normal;
- line-height: 14px;
- color: #000040;
- }
-div.banner {
- background: #bbbbcc;
- color: white;
- padding: 1;
- margin: 0;
- font-size: 90%;
- font-weight: bold;
- line-height: 1.1;
- text-align: center;
- width: 100%;
-}
-
--->
-</style>
-<base target="docwin">
-</head>
-<body>
-<div class="banner">%list_title%</div>
-START:entries
-<a href="%href%">%name%</a><br />
-END:entries
-</body></html>
-}
-
-CLASS_INDEX = FILE_INDEX
-METHOD_INDEX = FILE_INDEX
-
-INDEX = %{
-<html>
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-</head>
-
-<frameset cols="20%,*">
- <frameset rows="15%,35%,50%">
- <frame src="fr_file_index.html" title="Files" name="Files">
- <frame src="fr_class_index.html" name="Modules">
- <frame src="fr_method_index.html" name="Subroutines and Functions">
- </frameset>
- <frameset rows="80%,20%">
- <frame src="%initial_page%" name="docwin">
- <frame src="blank.html" name="source">
- </frameset>
- <noframes>
- <body bgcolor="#BBBBBB">
- Click <a href="html/index.html">here</a> for a non-frames
- version of this page.
- </body>
- </noframes>
-</frameset>
-
-</html>
-}
-
-# and a blank page to use as a target
-BLANK = %{
-<html><body bgcolor="#BBBBBB"></body></html>
-}
-
-def write_extra_pages
- template = TemplatePage.new(BLANK)
- File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
-end
-
-end
-end
diff --git a/lib/rdoc/generators/template/html/html.rb b/lib/rdoc/generators/template/html/html.rb
deleted file mode 100644
index 7f9e599465..0000000000
--- a/lib/rdoc/generators/template/html/html.rb
+++ /dev/null
@@ -1,711 +0,0 @@
-#
-# = CSS2 RDoc HTML template
-#
-# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a
-# bit more of the appearance of the output to cascading stylesheets than the
-# default. It was designed for clean inline code display, and uses DHTMl to
-# toggle the visbility of each method's source with each click on the '[source]'
-# link.
-#
-# == Authors
-#
-# * Michael Granger <ged@FaerieMUD.org>
-#
-# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
-#
-# This work is licensed under the Creative Commons Attribution License. To view
-# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
-# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
-# 94305, USA.
-#
-
-module RDoc
- module Page
-
- FONTS = "Verdana,Arial,Helvetica,sans-serif"
-
-STYLE = %{
-body {
- font-family: Verdana,Arial,Helvetica,sans-serif;
- font-size: 90%;
- margin: 0;
- margin-left: 40px;
- padding: 0;
- background: white;
-}
-
-h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
-h1 { font-size: 150%; }
-h2,h3,h4 { margin-top: 1em; }
-
-a { background: #eef; color: #039; text-decoration: none; }
-a:hover { background: #039; color: #eef; }
-
-/* Override the base stylesheet's Anchor inside a table cell */
-td > a {
- background: transparent;
- color: #039;
- text-decoration: none;
-}
-
-/* and inside a section title */
-.section-title > a {
- background: transparent;
- color: #eee;
- text-decoration: none;
-}
-
-/* === Structural elements =================================== */
-
-div#index {
- margin: 0;
- margin-left: -40px;
- padding: 0;
- font-size: 90%;
-}
-
-
-div#index a {
- margin-left: 0.7em;
-}
-
-div#index .section-bar {
- margin-left: 0px;
- padding-left: 0.7em;
- background: #ccc;
- font-size: small;
-}
-
-
-div#classHeader, div#fileHeader {
- width: auto;
- color: white;
- padding: 0.5em 1.5em 0.5em 1.5em;
- margin: 0;
- margin-left: -40px;
- border-bottom: 3px solid #006;
-}
-
-div#classHeader a, div#fileHeader a {
- background: inherit;
- color: white;
-}
-
-div#classHeader td, div#fileHeader td {
- background: inherit;
- color: white;
-}
-
-
-div#fileHeader {
- background: #057;
-}
-
-div#classHeader {
- background: #048;
-}
-
-
-.class-name-in-header {
- font-size: 180%;
- font-weight: bold;
-}
-
-
-div#bodyContent {
- padding: 0 1.5em 0 1.5em;
-}
-
-div#description {
- padding: 0.5em 1.5em;
- background: #efefef;
- border: 1px dotted #999;
-}
-
-div#description h1,h2,h3,h4,h5,h6 {
- color: #125;;
- background: transparent;
-}
-
-div#validator-badges {
- text-align: center;
-}
-div#validator-badges img { border: 0; }
-
-div#copyright {
- color: #333;
- background: #efefef;
- font: 0.75em sans-serif;
- margin-top: 5em;
- margin-bottom: 0;
- padding: 0.5em 2em;
-}
-
-
-/* === Classes =================================== */
-
-table.header-table {
- color: white;
- font-size: small;
-}
-
-.type-note {
- font-size: small;
- color: #DEDEDE;
-}
-
-.xxsection-bar {
- background: #eee;
- color: #333;
- padding: 3px;
-}
-
-.section-bar {
- color: #333;
- border-bottom: 1px solid #999;
- margin-left: -20px;
-}
-
-
-.section-title {
- background: #79a;
- color: #eee;
- padding: 3px;
- margin-top: 2em;
- margin-left: -30px;
- border: 1px solid #999;
-}
-
-.top-aligned-row { vertical-align: top }
-.bottom-aligned-row { vertical-align: bottom }
-
-/* --- Context section classes ----------------------- */
-
-.context-row { }
-.context-item-name { font-family: monospace; font-weight: bold; color: black; }
-.context-item-value { font-size: small; color: #448; }
-.context-item-desc { color: #333; padding-left: 2em; }
-
-/* --- Method classes -------------------------- */
-.method-detail {
- background: #efefef;
- padding: 0;
- margin-top: 0.5em;
- margin-bottom: 1em;
- border: 1px dotted #ccc;
-}
-.method-heading {
- color: black;
- background: #ccc;
- border-bottom: 1px solid #666;
- padding: 0.2em 0.5em 0 0.5em;
-}
-.method-signature { color: black; background: inherit; }
-.method-name { font-weight: bold; }
-.method-args { font-style: italic; }
-.method-description { padding: 0 0.5em 0 0.5em; }
-
-/* --- Source code sections -------------------- */
-
-a.source-toggle { font-size: 90%; }
-div.method-source-code {
- background: #262626;
- color: #ffdead;
- margin: 1em;
- padding: 0.5em;
- border: 1px dashed #999;
- overflow: hidden;
-}
-
-div.method-source-code pre { color: #ffdead; overflow: hidden; }
-
-/* --- Ruby keyword styles --------------------- */
-
-.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
-
-.ruby-constant { color: #7fffd4; background: transparent; }
-.ruby-keyword { color: #00ffff; background: transparent; }
-.ruby-ivar { color: #eedd82; background: transparent; }
-.ruby-operator { color: #00ffee; background: transparent; }
-.ruby-identifier { color: #ffdead; background: transparent; }
-.ruby-node { color: #ffa07a; background: transparent; }
-.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
-.ruby-regexp { color: #ffa07a; background: transparent; }
-.ruby-value { color: #7fffd4; background: transparent; }
-}
-
-
-#####################################################################
-### H E A D E R T E M P L A T E
-#####################################################################
-
-XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-}
-
-HEADER = XHTML_PREAMBLE + %{
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
- <meta http-equiv="Content-Script-Type" content="text/javascript" />
- <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
- <script type="text/javascript">
- // <![CDATA[
-
- function popupCode( url ) {
- window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
- }
-
- function toggleCode( id ) {
- if ( document.getElementById )
- elem = document.getElementById( id );
- else if ( document.all )
- elem = eval( "document.all." + id );
- else
- return false;
-
- elemStyle = elem.style;
-
- if ( elemStyle.display != "block" ) {
- elemStyle.display = "block"
- } else {
- elemStyle.display = "none"
- }
-
- return true;
- }
-
- // Make codeblocks hidden by default
- document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" )
-
- // ]]>
- </script>
-
-</head>
-<body>
-}
-
-
-#####################################################################
-### C O N T E X T C O N T E N T T E M P L A T E
-#####################################################################
-
-CONTEXT_CONTENT = %{
-}
-
-
-#####################################################################
-### F O O T E R T E M P L A T E
-#####################################################################
-FOOTER = %{
-<div id="validator-badges">
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
-</div>
-
-</body>
-</html>
-}
-
-
-#####################################################################
-### F I L E P A G E H E A D E R T E M P L A T E
-#####################################################################
-
-FILE_PAGE = %{
- <div id="fileHeader">
- <h1>%short_name%</h1>
- <table class="header-table">
- <tr class="top-aligned-row">
- <td><strong>Path:</strong></td>
- <td>%full_path%
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
- </td>
- </tr>
- <tr class="top-aligned-row">
- <td><strong>Last Update:</strong></td>
- <td>%dtm_modified%</td>
- </tr>
- </table>
- </div>
-}
-
-
-#####################################################################
-### C L A S S P A G E H E A D E R T E M P L A T E
-#####################################################################
-
-CLASS_PAGE = %{
- <div id="classHeader">
- <table class="header-table">
- <tr class="top-aligned-row">
- <td><strong>%classmod%</strong></td>
- <td class="class-name-in-header">%full_name%</td>
- </tr>
- <tr class="top-aligned-row">
- <td><strong>In:</strong></td>
- <td>
-START:infiles
-IF:full_path_url
- <a href="%full_path_url%">
-ENDIF:full_path_url
- %full_path%
-IF:full_path_url
- </a>
-ENDIF:full_path_url
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
- <br />
-END:infiles
- </td>
- </tr>
-
-IF:parent
- <tr class="top-aligned-row">
- <td><strong>Parent:</strong></td>
- <td>
-IF:par_url
- <a href="%par_url%">
-ENDIF:par_url
- %parent%
-IF:par_url
- </a>
-ENDIF:par_url
- </td>
- </tr>
-ENDIF:parent
- </table>
- </div>
-}
-
-
-#####################################################################
-### M E T H O D L I S T T E M P L A T E
-#####################################################################
-
-METHOD_LIST = %{
-
- <div id="contextContent">
-IF:diagram
- <div id="diagram">
- %diagram%
- </div>
-ENDIF:diagram
-
-IF:description
- <div id="description">
- %description%
- </div>
-ENDIF:description
-
-IF:requires
- <div id="requires-list">
- <h3 class="section-bar">Required files</h3>
-
- <div class="name-list">
-START:requires
- HREF:aref:name:&nbsp;&nbsp;
-END:requires
- </div>
- </div>
-ENDIF:requires
-
-IF:toc
- <div id="contents-list">
- <h3 class="section-bar">Contents</h3>
- <ul>
-START:toc
- <li><a href="#%href%">%secname%</a></li>
-END:toc
- </ul>
-ENDIF:toc
- </div>
-
-IF:methods
- <div id="method-list">
- <h3 class="section-bar">Methods</h3>
-
- <div class="name-list">
-START:methods
- HREF:aref:name:&nbsp;&nbsp;
-END:methods
- </div>
- </div>
-ENDIF:methods
-
- </div>
-
-
- <!-- if includes -->
-IF:includes
- <div id="includes">
- <h3 class="section-bar">Included Modules</h3>
-
- <div id="includes-list">
-START:includes
- <span class="include-name">HREF:aref:name:</span>
-END:includes
- </div>
- </div>
-ENDIF:includes
-
-START:sections
- <div id="section">
-IF:sectitle
- <h2 class="section-title"><a name="%secsequence%">%sectitle%</a></h2>
-IF:seccomment
- <div class="section-comment">
- %seccomment%
- </div>
-ENDIF:seccomment
-ENDIF:sectitle
-
-IF:classlist
- <div id="class-list">
- <h3 class="section-bar">Classes and Modules</h3>
-
- %classlist%
- </div>
-ENDIF:classlist
-
-IF:constants
- <div id="constants-list">
- <h3 class="section-bar">Constants</h3>
-
- <div class="name-list">
- <table summary="Constants">
-START:constants
- <tr class="top-aligned-row context-row">
- <td class="context-item-name">%name%</td>
- <td>=</td>
- <td class="context-item-value">%value%</td>
-IF:desc
- <td width="3em">&nbsp;</td>
- <td class="context-item-desc">%desc%</td>
-ENDIF:desc
- </tr>
-END:constants
- </table>
- </div>
- </div>
-ENDIF:constants
-
-IF:aliases
- <div id="aliases-list">
- <h3 class="section-bar">External Aliases</h3>
-
- <div class="name-list">
- <table summary="aliases">
-START:aliases
- <tr class="top-aligned-row context-row">
- <td class="context-item-name">%old_name%</td>
- <td>-&gt;</td>
- <td class="context-item-value">%new_name%</td>
- </tr>
-IF:desc
- <tr class="top-aligned-row context-row">
- <td>&nbsp;</td>
- <td colspan="2" class="context-item-desc">%desc%</td>
- </tr>
-ENDIF:desc
-END:aliases
- </table>
- </div>
- </div>
-ENDIF:aliases
-
-
-IF:attributes
- <div id="attribute-list">
- <h3 class="section-bar">Attributes</h3>
-
- <div class="name-list">
- <table>
-START:attributes
- <tr class="top-aligned-row context-row">
- <td class="context-item-name">%name%</td>
-IF:rw
- <td class="context-item-value">&nbsp;[%rw%]&nbsp;</td>
-ENDIF:rw
-IFNOT:rw
- <td class="context-item-value">&nbsp;&nbsp;</td>
-ENDIF:rw
- <td class="context-item-desc">%a_desc%</td>
- </tr>
-END:attributes
- </table>
- </div>
- </div>
-ENDIF:attributes
-
-
-
- <!-- if method_list -->
-IF:method_list
- <div id="methods">
-START:method_list
-IF:methods
- <h3 class="section-bar">%type% %category% methods</h3>
-
-START:methods
- <div id="method-%aref%" class="method-detail">
- <a name="%aref%"></a>
-
- <div class="method-heading">
-IF:codeurl
- <a href="%codeurl%" target="Code" class="method-signature"
- onclick="popupCode('%codeurl%');return false;">
-ENDIF:codeurl
-IF:sourcecode
- <a href="#%aref%" class="method-signature">
-ENDIF:sourcecode
-IF:callseq
- <span class="method-name">%callseq%</span>
-ENDIF:callseq
-IFNOT:callseq
- <span class="method-name">%name%</span><span class="method-args">%params%</span>
-ENDIF:callseq
-IF:codeurl
- </a>
-ENDIF:codeurl
-IF:sourcecode
- </a>
-ENDIF:sourcecode
- </div>
-
- <div class="method-description">
-IF:m_desc
- %m_desc%
-ENDIF:m_desc
-IF:sourcecode
- <p><a class="source-toggle" href="#"
- onclick="toggleCode('%aref%-source');return false;">[Source]</a></p>
- <div class="method-source-code" id="%aref%-source">
-<pre>
-%sourcecode%
-</pre>
- </div>
-ENDIF:sourcecode
- </div>
- </div>
-
-END:methods
-ENDIF:methods
-END:method_list
-
- </div>
-ENDIF:method_list
-END:sections
-}
-
-
-#####################################################################
-### B O D Y T E M P L A T E
-#####################################################################
-
-BODY = HEADER + %{
-
-!INCLUDE! <!-- banner header -->
-
- <div id="bodyContent">
-
-} + METHOD_LIST + %{
-
- </div>
-
-} + FOOTER
-
-
-
-#####################################################################
-### S O U R C E C O D E T E M P L A T E
-#####################################################################
-
-SRC_PAGE = XHTML_PREAMBLE + %{
-<html>
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
- <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
-</head>
-<body class="standalone-code">
- <pre>%code%</pre>
-</body>
-</html>
-}
-
-
-#####################################################################
-### I N D E X F I L E T E M P L A T E S
-#####################################################################
-
-FR_INDEX_BODY = %{
-!INCLUDE!
-}
-
-FILE_INDEX = XHTML_PREAMBLE + %{
-<!--
-
- %list_title%
-
- -->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>%list_title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
- <link rel="stylesheet" href="%style_url%" type="text/css" />
- <base target="docwin" />
-</head>
-<body>
-<div id="index">
- <h1 class="section-bar">%list_title%</h1>
- <div id="index-entries">
-START:entries
- <a href="%href%">%name%</a><br />
-END:entries
- </div>
-</div>
-</body>
-</html>
-}
-
-CLASS_INDEX = FILE_INDEX
-METHOD_INDEX = FILE_INDEX
-
-INDEX = %{<?xml version="1.0" encoding="%charset%"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
-
-<!--
-
- %title%
-
- -->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
-</head>
-<frameset rows="20%, 80%">
- <frameset cols="25%,35%,45%">
- <frame src="fr_file_index.html" title="Files" name="Files" />
- <frame src="fr_class_index.html" name="Classes" />
- <frame src="fr_method_index.html" name="Methods" />
- </frameset>
- <frame src="%initial_page%" name="docwin" />
-</frameset>
-</html>
-}
-
-
-
- end # module Page
-end # class RDoc
-
-require 'rdoc/generators/template/html/one_page_html'
diff --git a/lib/rdoc/generators/template/html/kilmer.rb b/lib/rdoc/generators/template/html/kilmer.rb
deleted file mode 100644
index 55071fc026..0000000000
--- a/lib/rdoc/generators/template/html/kilmer.rb
+++ /dev/null
@@ -1,435 +0,0 @@
-module RDoc
-module Page
-
-
-FONTS = "Verdana, Arial, Helvetica, sans-serif"
-
-STYLE = %{
-body,td,p { font-family: %fonts%;
- color: #000040;
-}
-
-.attr-rw { font-size: xx-small; color: #444488 }
-
-.title-row { background-color: #CCCCFF;
- color: #000010;
-}
-
-.big-title-font {
- color: black;
- font-weight: bold;
- font-family: %fonts%;
- font-size: large;
- height: 60px;
- padding: 10px 3px 10px 3px;
-}
-
-.small-title-font { color: black;
- font-family: %fonts%;
- font-size:10; }
-
-.aqua { color: black }
-
-.method-name, .attr-name {
- font-family: font-family: %fonts%;
- font-weight: bold;
- font-size: small;
- margin-left: 20px;
- color: #000033;
-}
-
-.tablesubtitle, .tablesubsubtitle {
- width: 100%;
- margin-top: 1ex;
- margin-bottom: .5ex;
- padding: 5px 0px 5px 3px;
- font-size: large;
- color: black;
- background-color: #CCCCFF;
- border: thin;
-}
-
-.name-list {
- margin-left: 5px;
- margin-bottom: 2ex;
- line-height: 105%;
-}
-
-.description {
- margin-left: 5px;
- margin-bottom: 2ex;
- line-height: 105%;
- font-size: small;
-}
-
-.methodtitle {
- font-size: small;
- font-weight: bold;
- text-decoration: none;
- color: #000033;
- background-color: white;
-}
-
-.srclink {
- font-size: small;
- font-weight: bold;
- text-decoration: none;
- color: #0000DD;
- background-color: white;
-}
-
-.paramsig {
- font-size: small;
-}
-
-.srcbut { float: right }
-
-}
-
-
-############################################################################
-
-
-BODY = %{
-<html><head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
- <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
- <script type="text/javascript" language="JavaScript">
- <!--
- function popCode(url) {
- parent.frames.source.location = url
- }
- //-->
- </script>
-</head>
-<body bgcolor="white">
-
-!INCLUDE! <!-- banner header -->
-
-IF:diagram
-<table width="100%"><tr><td align="center">
-%diagram%
-</td></tr></table>
-ENDIF:diagram
-
-IF:description
-<div class="description">%description%</div>
-ENDIF:description
-
-IF:requires
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Required files</td></tr>
-</table><br />
-<div class="name-list">
-START:requires
-HREF:aref:name:
-END:requires
-ENDIF:requires
-</div>
-
-IF:methods
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Methods</td></tr>
-</table><br />
-<div class="name-list">
-START:methods
-HREF:aref:name:,
-END:methods
-</div>
-ENDIF:methods
-
-
-START:sections
- <div id="section">
-IF:sectitle
- <h2 class="section-title"><a name="%secsequence%">%sectitle%</a></h2>
-IF:seccomment
- <div class="section-comment">
- %seccomment%
- </div>
-ENDIF:seccomment
-ENDIF:sectitle
-
-IF:attributes
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Attributes</td></tr>
-</table><br />
-<table cellspacing="5">
-START:attributes
- <tr valign="top">
-IF:rw
- <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
-ENDIF:rw
-IFNOT:rw
- <td></td>
-ENDIF:rw
- <td class="attr-name">%name%</td>
- <td>%a_desc%</td>
- </tr>
-END:attributes
-</table>
-ENDIF:attributes
-
-IF:classlist
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Classes and Modules</td></tr>
-</table><br />
-%classlist%<br />
-ENDIF:classlist
-
- !INCLUDE! <!-- method descriptions -->
-
-END:sections
-
-</body>
-</html>
-}
-
-###############################################################################
-
-FILE_PAGE = <<_FILE_PAGE_
-<table width="100%">
- <tr class="title-row">
- <td><table width="100%"><tr>
- <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font>%short_name%</td>
- <td align="right"><table cellspacing="0" cellpadding="2">
- <tr>
- <td class="small-title-font">Path:</td>
- <td class="small-title-font">%full_path%
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
- </td>
- </tr>
- <tr>
- <td class="small-title-font">Modified:</td>
- <td class="small-title-font">%dtm_modified%</td>
- </tr>
- </table>
- </td></tr></table></td>
- </tr>
-</table><br />
-_FILE_PAGE_
-
-###################################################################
-
-CLASS_PAGE = %{
-<table width="100%" border="0" cellspacing="0">
- <tr class="title-row">
- <td class="big-title-font">
- <font size="-3"><b>%classmod%</b><br /></font>%full_name%
- </td>
- <td align="right">
- <table cellspacing="0" cellpadding="2">
- <tr valign="top">
- <td class="small-title-font">In:</td>
- <td class="small-title-font">
-START:infiles
-HREF:full_path_url:full_path:
-IF:cvsurl
-&nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
-END:infiles
- </td>
- </tr>
-IF:parent
- <tr>
- <td class="small-title-font">Parent:</td>
- <td class="small-title-font">
-IF:par_url
- <a href="%par_url%" class="cyan">
-ENDIF:par_url
-%parent%
-IF:par_url
- </a>
-ENDIF:par_url
- </td>
- </tr>
-ENDIF:parent
- </table>
- </td>
- </tr>
-</table><br />
-}
-
-###################################################################
-
-METHOD_LIST = %{
-IF:includes
-<div class="tablesubsubtitle">Included modules</div><br />
-<div class="name-list">
-START:includes
- <span class="method-name">HREF:aref:name:</span>
-END:includes
-</div>
-ENDIF:includes
-
-IF:method_list
-START:method_list
-IF:methods
-<table cellpadding=5 width="100%">
-<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
-</table>
-START:methods
-<table width="100%" cellspacing="0" cellpadding="5" border="0">
-<tr><td class="methodtitle">
-<a name="%aref%">
-IF:callseq
-<b>%callseq%</b>
-ENDIF:callseq
-IFNOT:callseq
- <b>%name%</b>%params%
-ENDIF:callseq
-IF:codeurl
-<a href="%codeurl%" target="source" class="srclink">src</a>
-ENDIF:codeurl
-</a></td></tr>
-</table>
-IF:m_desc
-<div class="description">
-%m_desc%
-</div>
-ENDIF:m_desc
-IF:aka
-<div class="aka">
-This method is also aliased as
-START:aka
-<a href="%aref%">%name%</a>
-END:aka
-</div>
-ENDIF:aka
-IF:sourcecode
-<pre class="source">
-%sourcecode%
-</pre>
-ENDIF:sourcecode
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
-}
-
-=begin
-=end
-
-########################## Source code ##########################
-
-SRC_PAGE = %{
-<html>
-<head><title>%title%</title>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<style type="text/css">
-.ruby-comment { color: green; font-style: italic }
-.ruby-constant { color: #4433aa; font-weight: bold; }
-.ruby-identifier { color: #222222; }
-.ruby-ivar { color: #2233dd; }
-.ruby-keyword { color: #3333FF; font-weight: bold }
-.ruby-node { color: #777777; }
-.ruby-operator { color: #111111; }
-.ruby-regexp { color: #662222; }
-.ruby-value { color: #662222; font-style: italic }
- .kw { color: #3333FF; font-weight: bold }
- .cmt { color: green; font-style: italic }
- .str { color: #662222; font-style: italic }
- .re { color: #662222; }
-</style>
-</head>
-<body bgcolor="white">
-<pre>%code%</pre>
-</body>
-</html>
-}
-
-########################## Index ################################
-
-FR_INDEX_BODY = %{
-!INCLUDE!
-}
-
-FILE_INDEX = %{
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<style>
-<!--
- body {
-background-color: #ddddff;
- font-family: #{FONTS};
- font-size: 11px;
- font-style: normal;
- line-height: 14px;
- color: #000040;
- }
-div.banner {
- background: #0000aa;
- color: white;
- padding: 1;
- margin: 0;
- font-size: 90%;
- font-weight: bold;
- line-height: 1.1;
- text-align: center;
- width: 100%;
-}
-
--->
-</style>
-<base target="docwin">
-</head>
-<body>
-<div class="banner">%list_title%</div>
-START:entries
-<a href="%href%">%name%</a><br />
-END:entries
-</body></html>
-}
-
-CLASS_INDEX = FILE_INDEX
-METHOD_INDEX = FILE_INDEX
-
-INDEX = %{
-<html>
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-</head>
-
-<frameset cols="20%,*">
- <frameset rows="15%,35%,50%">
- <frame src="fr_file_index.html" title="Files" name="Files">
- <frame src="fr_class_index.html" name="Classes">
- <frame src="fr_method_index.html" name="Methods">
- </frameset>
-IF:inline_source
- <frame src="%initial_page%" name="docwin">
-ENDIF:inline_source
-IFNOT:inline_source
- <frameset rows="80%,20%">
- <frame src="%initial_page%" name="docwin">
- <frame src="blank.html" name="source">
- </frameset>
-ENDIF:inline_source
- <noframes>
- <body bgcolor="white">
- Click <a href="html/index.html">here</a> for a non-frames
- version of this page.
- </body>
- </noframes>
-</frameset>
-
-</html>
-}
-
-# and a blank page to use as a target
-BLANK = %{
-<html><body bgcolor="white"></body></html>
-}
-
-def write_extra_pages
- template = TemplatePage.new(BLANK)
- File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
-end
-
-end
-end
diff --git a/lib/rdoc/generators/template/html/old_html.rb b/lib/rdoc/generators/template/html/old_html.rb
deleted file mode 100644
index ca66302a08..0000000000
--- a/lib/rdoc/generators/template/html/old_html.rb
+++ /dev/null
@@ -1,728 +0,0 @@
-module RDoc
-
-# This is how you define the HTML that RDoc generates. Simply create
-# a file in rdoc/generators/html_templates that creates the
-# module RDoc::Page and populate it as described below. Then invoke
-# rdoc using the --template <name of your file> option, and
-# your template will be used.
-#
-# The constants defining pages use a simple templating system:
-#
-# * The templating system is passed a hash. Keys in the hash correspond
-# to tags on this page. The tag %abc% is looked up in the hash,
-# and is replaced by the corresponding hash value.
-#
-# * Some tags are optional. You can detect this using IF/ENDIF
-#
-# IF: title
-# The value of title is %title%
-# ENDIF: title
-#
-# * Some entries in the hash have values that are arrays, where each
-# entry in the array is itself a hash. These are used to generate
-# lists using the START: construct. For example, given a hash
-# containing
-#
-# { 'people' => [ { 'name' => 'Fred', 'age' => '12' },
-# { 'name' => 'Mary', 'age' => '21' } ]
-#
-# You could generate a simple table using
-#
-# <table>
-# START:people
-# <tr><td>%name%<td>%age%</tr>
-# END:people
-# </table>
-#
-# These lists can be nested to an arbitrary depth
-#
-# * the construct HREF:url:name: generates <a href="%url%">%name%</a>
-# if +url+ is defined in the hash, or %name% otherwise.
-#
-#
-# Your file must contain the following constants
-#
-# [*FONTS*] a list of fonts to be used
-# [*STYLE*] a CSS section (without the <style> or comments). This is
-# used to generate a style.css file
-#
-# [*BODY*]
-# The main body of all non-index RDoc pages. BODY will contain
-# two !INCLUDE!s. The first is used to include a document-type
-# specific header (FILE_PAGE or CLASS_PAGE). The second include
-# is for the method list (METHOD_LIST). THe body is passed:
-#
-# %title%::
-# the page's title
-#
-# %style_url%::
-# the url of a style sheet for this page
-#
-# %diagram%::
-# the optional URL of a diagram for this page
-#
-# %description%::
-# a (potentially multi-paragraph) string containing the
-# description for th file/class/module.
-#
-# %requires%::
-# an optional list of %aref%/%name% pairs, one for each module
-# required by this file.
-#
-# %methods%::
-# an optional list of %aref%/%name%, one for each method
-# documented on this page. This is intended to be an index.
-#
-# %attributes%::
-# An optional list. For each attribute it contains:
-# %name%:: the attribute name
-# %rw%:: r/o, w/o, or r/w
-# %a_desc%:: description of the attribute
-#
-# %classlist%::
-# An optional string containing an already-formatted list of
-# classes and modules documented in this file
-#
-# For FILE_PAGE entries, the body will be passed
-#
-# %short_name%::
-# The name of the file
-#
-# %full_path%::
-# The full path to the file
-#
-# %dtm_modified%::
-# The date/time the file was last changed
-#
-# For class and module pages, the body will be passed
-#
-# %classmod%::
-# The name of the class or module
-#
-# %files%::
-# A list. For each file this class is defined in, it contains:
-# %full_path_url%:: an (optional) URL of the RDoc page
-# for this file
-# %full_path%:: the name of the file
-#
-# %par_url%::
-# The (optional) URL of the RDoc page documenting this class's
-# parent class
-#
-# %parent%::
-# The name of this class's parent.
-#
-# For both files and classes, the body is passed the following information
-# on includes and methods:
-#
-# %includes%::
-# Optional list of included modules. For each, it receives
-# %aref%:: optional URL to RDoc page for the module
-# %name%:: the name of the module
-#
-# %method_list%::
-# Optional list of methods of a particular class and category.
-#
-# Each method list entry contains:
-#
-# %type%:: public/private/protected
-# %category%:: instance/class
-# %methods%:: a list of method descriptions
-#
-# Each method description contains:
-#
-# %aref%:: a target aref, used when referencing this method
-# description. You should code this as <a name="%aref%">
-# %codeurl%:: the optional URL to the page containing this method's
-# source code.
-# %name%:: the method's name
-# %params%:: the method's parameters
-# %callseq%:: a full calling sequence
-# %m_desc%:: the (potentially multi-paragraph) description of
-# this method.
-#
-# [*CLASS_PAGE*]
-# Header for pages documenting classes and modules. See
-# BODY above for the available parameters.
-#
-# [*FILE_PAGE*]
-# Header for pages documenting files. See
-# BODY above for the available parameters.
-#
-# [*METHOD_LIST*]
-# Controls the display of the listing of methods. See BODY for
-# parameters.
-#
-# [*INDEX*]
-# The top-level index page. For a browser-like environment
-# define a frame set that includes the file, class, and
-# method indices. Passed
-# %title%:: title of page
-# %initial_page% :: url of initial page to display
-#
-# [*CLASS_INDEX*]
-# Individual files for the three indexes. Passed:
-# %index_url%:: URL of main index page
-# %entries%:: List of
-# %name%:: name of an index entry
-# %href%:: url of corresponding page
-# [*METHOD_INDEX*]
-# Same as CLASS_INDEX for methods
-#
-# [*FILE_INDEX*]
-# Same as CLASS_INDEX for methods
-#
-# [*FR_INDEX_BODY*]
-# A wrapper around CLASS_INDEX, METHOD_INDEX, and FILE_INDEX.
-# If those index strings contain the complete HTML for the
-# output, then FR_INDEX_BODY can simply be !INCLUDE!
-#
-# [*SRC_PAGE*]
-# Page used to display source code. Passed %title% and %code%,
-# the latter being a multi-line string of code.
-
-module Page
-
-FONTS = "Verdana, Arial, Helvetica, sans-serif"
-
-STYLE = %{
-body,td,p { font-family: %fonts%;
- color: #000040;
-}
-
-.attr-rw { font-size: x-small; color: #444488 }
-
-.title-row { background: #0000aa;
- color: #eeeeff;
-}
-
-.big-title-font { color: white;
- font-family: %fonts%;
- font-size: large;
- height: 50px}
-
-.small-title-font { color: aqua;
- font-family: %fonts%;
- font-size: xx-small; }
-
-.aqua { color: aqua }
-
-.method-name, attr-name {
- font-family: monospace; font-weight: bold;
-}
-
-.tablesubtitle, .tablesubsubtitle {
- width: 100%;
- margin-top: 1ex;
- margin-bottom: .5ex;
- padding: 5px 0px 5px 20px;
- font-size: large;
- color: aqua;
- background: #3333cc;
-}
-
-.name-list {
- font-family: monospace;
- margin-left: 40px;
- margin-bottom: 2ex;
- line-height: 140%;
-}
-
-.description {
- margin-left: 40px;
- margin-top: -2ex;
- margin-bottom: 2ex;
-}
-
-.description p {
- line-height: 140%;
-}
-
-.aka {
- margin-left: 40px;
- margin-bottom: 2ex;
- line-height: 100%;
- font-size: small;
- color: #808080;
-}
-
-.methodtitle {
- font-size: medium;
- text-decoration: none;
- color: #0000AA;
- background: white;
-}
-
-.paramsig {
- font-size: small;
-}
-
-.srcbut { float: right }
-
-pre { font-size: 1.2em; }
-tt { font-size: 1.2em; }
-
-pre.source {
- border-style: groove;
- background-color: #ddddff;
- margin-left: 40px;
- padding: 1em 0em 1em 2em;
-}
-
-.classlist {
- margin-left: 40px;
- margin-bottom: 2ex;
- line-height: 140%;
-}
-
-li {
- display: list-item;
- margin-top: .6em;
-}
-
-.ruby-comment { color: green; font-style: italic }
-.ruby-constant { color: #4433aa; font-weight: bold; }
-.ruby-identifier { color: #222222; }
-.ruby-ivar { color: #2233dd; }
-.ruby-keyword { color: #3333FF; font-weight: bold }
-.ruby-node { color: #777777; }
-.ruby-operator { color: #111111; }
-.ruby-regexp { color: #662222; }
-.ruby-value { color: #662222; font-style: italic }
-
-}
-
-
-############################################################################
-
-
-HEADER = %{
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
- <link rel=StyleSheet href="%style_url%" type="text/css" media="screen" />
- <script type="text/javascript" language="JavaScript">
- <!--
- function popCode(url) {
- window.open(url, "Code",
- "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
- }
- //-->
- </script>
-</head>
-}
-
-
-###################################################################
-
-METHOD_LIST = %{
-IF:includes
-<table summary="Included modules" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Included modules</td></tr>
-</table>
-<div class="name-list">
-START:includes
- <span class="method-name">HREF:aref:name:</span>
-END:includes
-</div>
-ENDIF:includes
-
-IF:method_list
-START:method_list
-IF:methods
-<table summary="Method list" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
-</table>
-START:methods
-<table summary="method" width="100%" cellspacing="0" cellpadding="5" border="0">
-<tr><td class="methodtitle">
-<a name="%aref%"></a>
-IF:codeurl
-<a href="%codeurl%" target="Code" class="methodtitle"
- onClick="popCode('%codeurl%');return false;">
-ENDIF:codeurl
-IF:callseq
-<b>%callseq%</b>
-ENDIF:callseq
-IFNOT:callseq
-<b>%name%</b>%params%
-ENDIF:callseq
-IF:codeurl
-</a>
-ENDIF:codeurl
-</td></tr>
-</table>
-IF:m_desc
-<div class="description">
-%m_desc%
-</div>
-ENDIF:m_desc
-IF:aka
-<div class="aka">
-This method is also aliased as
-START:aka
-<a href="%aref%">%name%</a>
-END:aka
-</div>
-ENDIF:aka
-IF:sourcecode
-<pre class="source">
-%sourcecode%
-</pre>
-ENDIF:sourcecode
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
-}
-
-###################################################################
-
-CONTEXT_CONTENT = %{
-IF:diagram
-<table summary="Diagram of classes and modules" width="100%">
-<tr><td align="center">
-%diagram%
-</td></tr></table>
-ENDIF:diagram
-
-
-IF:description
-<div class="description">%description%</div>
-ENDIF:description
-
-IF:requires
-<table summary="Requires" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Required files</td></tr>
-</table>
-<div class="name-list">
-START:requires
-HREF:aref:name:&nbsp; &nbsp;
-END:requires
-</div>
-ENDIF:requires
-
-IF:methods
-<table summary="Methods" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Methods</td></tr>
-</table>
-<div class="name-list">
-START:methods
-HREF:aref:name:&nbsp; &nbsp;
-END:methods
-</div>
-ENDIF:methods
-
-IF:constants
-<table summary="Constants" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Constants</td></tr>
-</table>
-<table cellpadding="5">
-START:constants
-<tr valign="top"><td>%name%</td><td>=</td><td>%value%</td></tr>
-IF:desc
-<tr><td></td><td></td><td>%desc%</td></tr>
-ENDIF:desc
-END:constants
-</table>
-ENDIF:constants
-
-IF:aliases
-<table summary="Aliases" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">External Aliases</td></tr>
-</table>
-<div class="name-list">
-START:aliases
-%old_name% -> %new_name%<br />
-END:aliases
-</div>
-ENDIF:aliases
-
-IF:attributes
-<table summary="Attributes" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Attributes</td></tr>
-</table>
-<table summary="Attribute details" cellspacing="5">
-START:attributes
- <tr valign="top">
- <td class="attr-name">%name%</td>
-IF:rw
- <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
-ENDIF:rw
-IFNOT:rw
- <td></td>
-ENDIF:rw
- <td>%a_desc%</td>
- </tr>
-END:attributes
-</table>
-ENDIF:attributes
-
-IF:classlist
-<table summary="List of classes" cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Classes and Modules</td></tr>
-</table>
-<div class="classlist">
-%classlist%
-</div>
-ENDIF:classlist
-}
-
-###############################################################################
-
-BODY = HEADER + %{
-<body bgcolor="white">
-!INCLUDE! <!-- banner header -->
-} +
-CONTEXT_CONTENT + METHOD_LIST +
-%{
-</body>
-</html>
-}
-
-
-###############################################################################
-
-FILE_PAGE = <<_FILE_PAGE_
-<table summary="Information on file" width="100%">
- <tr class="title-row">
- <td><table summary="layout" width="100%"><tr>
- <td class="big-title-font" colspan="2">%short_name%</td>
- <td align="right"><table summary="layout" cellspacing="0" cellpadding="2">
- <tr>
- <td class="small-title-font">Path:</td>
- <td class="small-title-font">%full_path%
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
- </td>
- </tr>
- <tr>
- <td class="small-title-font">Modified:</td>
- <td class="small-title-font">%dtm_modified%</td>
- </tr>
- </table>
- </td></tr></table></td>
- </tr>
-</table>
-_FILE_PAGE_
-
-###################################################################
-
-CLASS_PAGE = %{
-<table summary="Information on class" width="100%" border="0" cellspacing="0">
- <tr class="title-row">
- <td class="big-title-font">
- <sup><font color="aqua">%classmod%</font></sup> %full_name%
- </td>
- <td align="right">
- <table summary="layout" cellspacing="0" cellpadding="2">
- <tr valign="top">
- <td class="small-title-font">In:</td>
- <td class="small-title-font">
-START:infiles
-IF:full_path_url
- <a href="%full_path_url%" class="aqua">
-ENDIF:full_path_url
-%full_path%
-IF:full_path_url
- </a>
-ENDIF:full_path_url
-IF:cvsurl
- &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-ENDIF:cvsurl
-<br />
-END:infiles
- </td>
- </tr>
-IF:parent
- <tr>
- <td class="small-title-font">Parent:</td>
- <td class="small-title-font">
-IF:par_url
- <a href="%par_url%" class="aqua">
-ENDIF:par_url
-%parent%
-IF:par_url
- </a>
-ENDIF:par_url
- </td>
- </tr>
-ENDIF:parent
- </table>
- </td>
- </tr>
-</table>
-}
-
-=begin
-=end
-
-########################## Source code ##########################
-
-SRC_PAGE = %{
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<title>%title%</title>
-<link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
-</head>
-<body bgcolor="white">
-<pre>%code%</pre>
-</body>
-</html>
-}
-
-########################## Index ################################
-
-FR_INDEX_BODY = %{
-!INCLUDE!
-}
-
-FILE_INDEX = %{
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<title>%list_title%</title>
-<style type="text/css">
-<!--
- body {
-background-color: #ddddff;
- font-family: #{FONTS};
- font-size: 11px;
- font-style: normal;
- line-height: 14px;
- color: #000040;
- }
-div.banner {
- background: #0000aa;
- color: white;
- padding: 1;
- margin: 0;
- font-size: 90%;
- font-weight: bold;
- line-height: 1.1;
- text-align: center;
- width: 100%;
-}
-
-A.xx { color: white; font-weight: bold; }
--->
-</style>
-<base target="docwin">
-</head>
-<body>
-<div class="banner"><a href="%index_url%" class="xx">%list_title%</a></div>
-START:entries
-<a href="%href%">%name%</a><br />
-END:entries
-</body></html>
-}
-
-CLASS_INDEX = FILE_INDEX
-METHOD_INDEX = FILE_INDEX
-
-INDEX = %{
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
-<title>%title%</title></head>
-
-<frameset rows="20%, 80%">
- <frameset cols="25%,35%,45%">
- <frame src="fr_file_index.html" title="Files" name="Files">
- <frame src="fr_class_index.html" name="Classes">
- <frame src="fr_method_index.html" name="Methods">
- </frameset>
- <frame src="%initial_page%" name="docwin">
- <noframes>
- <body bgcolor="white">
- Sorry, RDoc currently only generates HTML using frames.
- </body>
- </noframes>
-</frameset>
-
-</html>
-}
-
-######################################################################
-#
-# The following is used for the -1 option
-#
-
-CONTENTS_XML = %{
-IF:description
-%description%
-ENDIF:description
-
-IF:requires
-<h4>Requires:</h4>
-<ul>
-START:requires
-IF:aref
-<li><a href="%aref%">%name%</a></li>
-ENDIF:aref
-IFNOT:aref
-<li>%name%</li>
-ENDIF:aref
-END:requires
-</ul>
-ENDIF:requires
-
-IF:attributes
-<h4>Attributes</h4>
-<table>
-START:attributes
-<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
-END:attributes
-</table>
-ENDIF:attributes
-
-IF:includes
-<h4>Includes</h4>
-<ul>
-START:includes
-IF:aref
-<li><a href="%aref%">%name%</a></li>
-ENDIF:aref
-IFNOT:aref
-<li>%name%</li>
-ENDIF:aref
-END:includes
-</ul>
-ENDIF:includes
-
-IF:method_list
-<h3>Methods</h3>
-START:method_list
-IF:methods
-START:methods
-<h4>%type% %category% method: <a name="%aref%">%name%%params%</a></h4>
-
-IF:m_desc
-%m_desc%
-ENDIF:m_desc
-
-IF:sourcecode
-<blockquote><pre>
-%sourcecode%
-</pre></blockquote>
-ENDIF:sourcecode
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
-}
-
-
-end
-end
-
-require 'rdoc/generators/template/html/one_page_html'
diff --git a/lib/rdoc/generators/template/html/one_page_html.rb b/lib/rdoc/generators/template/html/one_page_html.rb
deleted file mode 100644
index 19441f4725..0000000000
--- a/lib/rdoc/generators/template/html/one_page_html.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-module RDoc
-module Page
-######################################################################
-#
-# The following is used for the -1 option
-#
-
-CONTENTS_XML = %{
-IF:description
-%description%
-ENDIF:description
-
-IF:requires
-<h4>Requires:</h4>
-<ul>
-START:requires
-IF:aref
-<li><a href="%aref%">%name%</a></li>
-ENDIF:aref
-IFNOT:aref
-<li>%name%</li>
-ENDIF:aref
-END:requires
-</ul>
-ENDIF:requires
-
-IF:attributes
-<h4>Attributes</h4>
-<table>
-START:attributes
-<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
-END:attributes
-</table>
-ENDIF:attributes
-
-IF:includes
-<h4>Includes</h4>
-<ul>
-START:includes
-IF:aref
-<li><a href="%aref%">%name%</a></li>
-ENDIF:aref
-IFNOT:aref
-<li>%name%</li>
-ENDIF:aref
-END:includes
-</ul>
-ENDIF:includes
-
-IF:method_list
-<h3>Methods</h3>
-START:method_list
-IF:methods
-START:methods
-<h4>%type% %category% method:
-IF:callseq
-<a name="%aref%">%callseq%</a>
-ENDIF:callseq
-IFNOT:callseq
-<a name="%aref%">%name%%params%</a></h4>
-ENDIF:callseq
-
-IF:m_desc
-%m_desc%
-ENDIF:m_desc
-
-IF:sourcecode
-<blockquote><pre>
-%sourcecode%
-</pre></blockquote>
-ENDIF:sourcecode
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
-}
-
-########################################################################
-
-ONE_PAGE = %{
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
- <title>%title%</title>
- <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
-</head>
-<body>
-START:files
-<h2>File: %short_name%</h2>
-<table>
- <tr><td>Path:</td><td>%full_path%</td></tr>
- <tr><td>Modified:</td><td>%dtm_modified%</td></tr>
-</table>
-} + CONTENTS_XML + %{
-END:files
-
-IF:classes
-<h2>Classes</h2>
-START:classes
-IF:parent
-<h3>%classmod% %full_name% &lt; HREF:par_url:parent:</h3>
-ENDIF:parent
-IFNOT:parent
-<h3>%classmod% %full_name%</h3>
-ENDIF:parent
-
-IF:infiles
-(in files
-START:infiles
-HREF:full_path_url:full_path:
-END:infiles
-)
-ENDIF:infiles
-} + CONTENTS_XML + %{
-END:classes
-ENDIF:classes
-</body>
-</html>
-}
-
-end
-end
diff --git a/lib/rdoc/generators/template/xml/rdf.rb b/lib/rdoc/generators/template/xml/rdf.rb
deleted file mode 100644
index 1545d81a2f..0000000000
--- a/lib/rdoc/generators/template/xml/rdf.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-module RDoc
-module Page
-
-
-
-CONTENTS_RDF = %{
-IF:description
- <description rd:parseType="Literal">
-%description%
- </description>
-ENDIF:description
-
-IF:requires
-START:requires
- <rd:required-file rd:name="%name%" />
-END:requires
-ENDIF:requires
-
-IF:attributes
-START:attributes
- <contents>
- <Attribute rd:name="%name%">
-IF:rw
- <attribute-rw>%rw%</attribute-rw>
-ENDIF:rw
- <description rdf:parseType="Literal">%a_desc%</description>
- </Attribute>
- </contents>
-END:attributes
-ENDIF:attributes
-
-IF:includes
- <IncludedModuleList>
-START:includes
- <included-module rd:name="%name%" />
-END:includes
- </IncludedModuleList>
-ENDIF:includes
-
-IF:method_list
-START:method_list
-IF:methods
-START:methods
- <contents>
- <Method rd:name="%name%" rd:visibility="%type%"
- rd:category="%category%" rd:id="%aref%">
- <parameters>%params%</parameters>
-IF:m_desc
- <description rdf:parseType="Literal">
-%m_desc%
- </description>
-ENDIF:m_desc
-IF:sourcecode
- <source-code-listing rdf:parseType="Literal">
-%sourcecode%
- </source-code-listing>
-ENDIF:sourcecode
- </Method>
- </contents>
-END:methods
-ENDIF:methods
-END:method_list
-ENDIF:method_list
- <!-- end method list -->
-}
-
-########################################################################
-
-ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://pragprog.com/rdoc/rdoc.rdf#"
- xmlns:rd="http://pragprog.com/rdoc/rdoc.rdf#">
-
-<!-- RDoc -->
-START:files
- <rd:File rd:name="%short_name%" rd:id="%href%">
- <path>%full_path%</path>
- <dtm-modified>%dtm_modified%</dtm-modified>
-} + CONTENTS_RDF + %{
- </rd:File>
-END:files
-START:classes
- <%classmod% rd:name="%full_name%" rd:id="%full_name%">
- <classmod-info>
-IF:infiles
- <InFiles>
-START:infiles
- <infile>
- <File rd:name="%full_path%"
-IF:full_path_url
- rdf:about="%full_path_url%"
-ENDIF:full_path_url
- />
- </infile>
-END:infiles
- </InFiles>
-ENDIF:infiles
-IF:parent
- <superclass>HREF:par_url:parent:</superclass>
-ENDIF:parent
- </classmod-info>
-} + CONTENTS_RDF + %{
- </%classmod%>
-END:classes
-<!-- /RDoc -->
-</rdf:RDF>
-}
-
-
-end
-end
-
diff --git a/lib/rdoc/generators/template/xml/xml.rb b/lib/rdoc/generators/template/xml/xml.rb
deleted file mode 100644
index 4a0c8c9ac4..0000000000
--- a/lib/rdoc/generators/template/xml/xml.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-module RDoc
-module Page
-
-
-
-CONTENTS_XML = %{
-IF:description
- <description>
-%description%
- </description>
-ENDIF:description
- <contents>
-IF:requires
- <required-file-list>
-START:requires
- <required-file name="%name%"
-IF:aref
- href="%aref%"
-ENDIF:aref
- />
-END:requires
- </required-file-list>
-ENDIF:requires
-IF:attributes
- <attribute-list>
-START:attributes
- <attribute name="%name%">
-IF:rw
- <attribute-rw>%rw%</attribute-rw>
-ENDIF:rw
- <description>%a_desc%</description>
- </attribute>
-END:attributes
- </attribute-list>
-ENDIF:attributes
-IF:includes
- <included-module-list>
-START:includes
- <included-module name="%name%"
-IF:aref
- href="%aref%"
-ENDIF:aref
- />
-END:includes
- </included-module-list>
-ENDIF:includes
-IF:method_list
- <method-list>
-START:method_list
-IF:methods
-START:methods
- <method name="%name%" type="%type%" category="%category%" id="%aref%">
- <parameters>%params%</parameters>
-IF:m_desc
- <description>
-%m_desc%
- </description>
-ENDIF:m_desc
-IF:sourcecode
- <source-code-listing>
-%sourcecode%
- </source-code-listing>
-ENDIF:sourcecode
- </method>
-END:methods
-ENDIF:methods
-END:method_list
- </method-list>
-ENDIF:method_list
- </contents>
-}
-
-########################################################################
-
-ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
-<rdoc>
-<file-list>
-START:files
- <file name="%short_name%" id="%href%">
- <file-info>
- <path>%full_path%</path>
- <dtm-modified>%dtm_modified%</dtm-modified>
- </file-info>
-} + CONTENTS_XML + %{
- </file>
-END:files
-</file-list>
-<class-module-list>
-START:classes
- <%classmod% name="%full_name%" id="%full_name%">
- <classmod-info>
-IF:infiles
- <infiles>
-START:infiles
- <infile>HREF:full_path_url:full_path:</infile>
-END:infiles
- </infiles>
-ENDIF:infiles
-IF:parent
- <superclass>HREF:par_url:parent:</superclass>
-ENDIF:parent
- </classmod-info>
-} + CONTENTS_XML + %{
- </%classmod%>
-END:classes
-</class-module-list>
-</rdoc>
-}
-
-
-end
-end
diff --git a/lib/rdoc/generators/xml_generator.rb b/lib/rdoc/generators/xml_generator.rb
deleted file mode 100644
index 8c1a76d62b..0000000000
--- a/lib/rdoc/generators/xml_generator.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-
-require 'ftools'
-
-require 'rdoc/options'
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_html'
-require 'rdoc/generators/html_generator'
-
-module Generators
-
- # Generate XML output as one big file
-
- class XMLGenerator < HTMLGenerator
-
- # Standard generator factory
- def XMLGenerator.for(options)
- XMLGenerator.new(options)
- end
-
-
- def initialize(*args)
- super
- end
-
- ##
- # Build the initial indices and output objects
- # based on an array of TopLevel objects containing
- # the extracted information.
-
- def generate(info)
- @info = info
- @files = []
- @classes = []
- @hyperlinks = {}
-
- build_indices
- generate_xml
- end
-
-
- ##
- # Generate:
- #
- # * a list of HtmlFile objects for each TopLevel object.
- # * a list of HtmlClass objects for each first level
- # class or module in the TopLevel objects
- # * a complete list of all hyperlinkable terms (file,
- # class, module, and method names)
-
- def build_indices
-
- @info.each do |toplevel|
- @files << HtmlFile.new(toplevel, @options, FILE_DIR)
- end
-
- RDoc::TopLevel.all_classes_and_modules.each do |cls|
- build_class_list(cls, @files[0], CLASS_DIR)
- end
- end
-
- def build_class_list(from, html_file, class_dir)
- @classes << HtmlClass.new(from, html_file, class_dir, @options)
- from.each_classmodule do |mod|
- build_class_list(mod, html_file, class_dir)
- end
- end
-
- ##
- # Generate all the HTML. For the one-file case, we generate
- # all the information in to one big hash
- #
- def generate_xml
- values = {
- 'charset' => @options.charset,
- 'files' => gen_into(@files),
- 'classes' => gen_into(@classes)
- }
-
- # this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
-
- template = TemplatePage.new(RDoc::Page::ONE_PAGE)
-
- if @options.op_name
- opfile = File.open(@options.op_name, "w")
- else
- opfile = $stdout
- end
- template.write_html_on(opfile, values)
- end
-
- def gen_into(list)
- res = []
- list.each do |item|
- res << item.value_hash
- end
- res
- end
-
- def gen_file_index
- gen_an_index(@files, 'Files')
- end
-
- def gen_class_index
- gen_an_index(@classes, 'Classes')
- end
-
- def gen_method_index
- gen_an_index(HtmlMethod.all_methods, 'Methods')
- end
-
-
- def gen_an_index(collection, title)
- res = []
- collection.sort.each do |f|
- if f.document_self
- res << { "href" => f.path, "name" => f.index_name }
- end
- end
-
- return {
- "entries" => res,
- 'list_title' => title,
- 'index_url' => main_url,
- }
- end
-
- end
-
-end
diff --git a/lib/rdoc/ghost_method.rb b/lib/rdoc/ghost_method.rb
new file mode 100644
index 0000000000..2488feb9d7
--- /dev/null
+++ b/lib/rdoc/ghost_method.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+##
+# GhostMethod represents a method referenced only by a comment
+
+class RDoc::GhostMethod < RDoc::AnyMethod
+end
+
diff --git a/lib/rdoc/i18n.rb b/lib/rdoc/i18n.rb
new file mode 100644
index 0000000000..4cb5986155
--- /dev/null
+++ b/lib/rdoc/i18n.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+##
+# This module provides i18n related features.
+
+module RDoc::I18n
+
+ autoload :Locale, 'rdoc/i18n/locale'
+ autoload :Text, 'rdoc/i18n/text'
+
+end
diff --git a/lib/rdoc/i18n/locale.rb b/lib/rdoc/i18n/locale.rb
new file mode 100644
index 0000000000..6a70d6c986
--- /dev/null
+++ b/lib/rdoc/i18n/locale.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+##
+# A message container for a locale.
+#
+# This object provides the following two features:
+#
+# * Loads translated messages from .po file.
+# * Translates a message into the locale.
+
+class RDoc::I18n::Locale
+
+ @@locales = {} # :nodoc:
+
+ class << self
+
+ ##
+ # Returns the locale object for +locale_name+.
+
+ def [](locale_name)
+ @@locales[locale_name] ||= new(locale_name)
+ end
+
+ ##
+ # Sets the locale object for +locale_name+.
+ #
+ # Normally, this method is not used. This method is useful for
+ # testing.
+
+ def []=(locale_name, locale)
+ @@locales[locale_name] = locale
+ end
+
+ end
+
+ ##
+ # The name of the locale. It uses IETF language tag format
+ # +[language[_territory][.codeset][@modifier]]+.
+ #
+ # See also {BCP 47 - Tags for Identifying
+ # Languages}[http://tools.ietf.org/rfc/bcp/bcp47.txt].
+
+ attr_reader :name
+
+ ##
+ # Creates a new locale object for +name+ locale. +name+ must
+ # follow IETF language tag format.
+
+ def initialize(name)
+ @name = name
+ @messages = {}
+ end
+
+ ##
+ # Loads translation messages from +locale_directory+/+@name+/rdoc.po
+ # or +locale_directory+/+@name+.po. The former has high priority.
+ #
+ # This method requires gettext gem for parsing .po file. If you
+ # don't have gettext gem, this method doesn't load .po file. This
+ # method warns and returns +false+.
+ #
+ # Returns +true+ if succeeded, +false+ otherwise.
+
+ def load(locale_directory)
+ return false if @name.nil?
+
+ po_file_candidates = [
+ File.join(locale_directory, @name, 'rdoc.po'),
+ File.join(locale_directory, "#{@name}.po"),
+ ]
+ po_file = po_file_candidates.find do |po_file_candidate|
+ File.exist?(po_file_candidate)
+ end
+ return false unless po_file
+
+ begin
+ require 'gettext/po_parser'
+ require 'gettext/mo'
+ rescue LoadError
+ warn('Need gettext gem for i18n feature:')
+ warn(' gem install gettext')
+ return false
+ end
+
+ po_parser = GetText::POParser.new
+ messages = GetText::MO.new
+ po_parser.report_warning = false
+ po_parser.parse_file(po_file, messages)
+
+ @messages.merge!(messages)
+
+ true
+ end
+
+ ##
+ # Translates the +message+ into locale. If there is no translation
+ # messages for +message+ in locale, +message+ itself is returned.
+
+ def translate(message)
+ @messages[message] || message
+ end
+
+end
diff --git a/lib/rdoc/i18n/text.rb b/lib/rdoc/i18n/text.rb
new file mode 100644
index 0000000000..7ea6664442
--- /dev/null
+++ b/lib/rdoc/i18n/text.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+##
+# An i18n supported text.
+#
+# This object provides the following two features:
+#
+# * Extracts translation messages from wrapped raw text.
+# * Translates wrapped raw text in specified locale.
+#
+# Wrapped raw text is one of String, RDoc::Comment or Array of them.
+
+class RDoc::I18n::Text
+
+ ##
+ # Creates a new i18n supported text for +raw+ text.
+
+ def initialize(raw)
+ @raw = raw
+ end
+
+ ##
+ # Extracts translation target messages and yields each message.
+ #
+ # Each yielded message is a Hash. It consists of the followings:
+ #
+ # :type :: :paragraph
+ # :paragraph :: String (The translation target message itself.)
+ # :line_no :: Integer (The line number of the :paragraph is started.)
+ #
+ # The above content may be added in the future.
+
+ def extract_messages
+ parse do |part|
+ case part[:type]
+ when :empty_line
+ # ignore
+ when :paragraph
+ yield(part)
+ end
+ end
+ end
+
+ # Translates raw text into +locale+.
+ def translate(locale)
+ translated_text = ''
+ parse do |part|
+ case part[:type]
+ when :paragraph
+ translated_text += locale.translate(part[:paragraph])
+ when :empty_line
+ translated_text += part[:line]
+ else
+ raise "should not reach here: unexpected type: #{type}"
+ end
+ end
+ translated_text
+ end
+
+ private
+ def parse(&block)
+ paragraph = ''
+ paragraph_start_line = 0
+ line_no = 0
+
+ each_line(@raw) do |line|
+ line_no += 1
+ case line
+ when /\A\s*\z/
+ if paragraph.empty?
+ emit_empty_line_event(line, line_no, &block)
+ else
+ paragraph += line
+ emit_paragraph_event(paragraph, paragraph_start_line, line_no,
+ &block)
+ paragraph = ''
+ end
+ else
+ paragraph_start_line = line_no if paragraph.empty?
+ paragraph += line
+ end
+ end
+
+ unless paragraph.empty?
+ emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block)
+ end
+ end
+
+ def each_line(raw, &block)
+ case raw
+ when RDoc::Comment
+ raw.text.each_line(&block)
+ when Array
+ raw.each do |comment, location|
+ each_line(comment, &block)
+ end
+ else
+ raw.each_line(&block)
+ end
+ end
+
+ def emit_empty_line_event(line, line_no)
+ part = {
+ :type => :empty_line,
+ :line => line,
+ :line_no => line_no,
+ }
+ yield(part)
+ end
+
+ def emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block)
+ paragraph_part = {
+ :type => :paragraph,
+ :line_no => paragraph_start_line,
+ }
+ match_data = /(\s*)\z/.match(paragraph)
+ if match_data
+ paragraph_part[:paragraph] = match_data.pre_match
+ yield(paragraph_part)
+ emit_empty_line_event(match_data[1], line_no, &block)
+ else
+ paragraph_part[:paragraph] = paragraph
+ yield(paragraph_part)
+ end
+ end
+
+end
diff --git a/lib/rdoc/include.rb b/lib/rdoc/include.rb
new file mode 100644
index 0000000000..b3ad610649
--- /dev/null
+++ b/lib/rdoc/include.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+##
+# A Module included in a class with \#include
+#
+# RDoc::Include.new 'Enumerable', 'comment ...'
+
+class RDoc::Include < RDoc::Mixin
+
+end
+
diff --git a/lib/rdoc/known_classes.rb b/lib/rdoc/known_classes.rb
new file mode 100644
index 0000000000..4d7f4aa995
--- /dev/null
+++ b/lib/rdoc/known_classes.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+module RDoc
+
+ ##
+ # Ruby's built-in classes, modules and exceptions
+
+ KNOWN_CLASSES = {
+ "rb_cArray" => "Array",
+ "rb_cBasicObject" => "BasicObject",
+ "rb_cBignum" => "Bignum",
+ "rb_cClass" => "Class",
+ "rb_cData" => "Data",
+ "rb_cDir" => "Dir",
+ "rb_cEncoding" => "Encoding",
+ "rb_cFalseClass" => "FalseClass",
+ "rb_cFile" => "File",
+ "rb_cFixnum" => "Fixnum",
+ "rb_cFloat" => "Float",
+ "rb_cHash" => "Hash",
+ "rb_cIO" => "IO",
+ "rb_cInteger" => "Integer",
+ "rb_cModule" => "Module",
+ "rb_cNilClass" => "NilClass",
+ "rb_cNumeric" => "Numeric",
+ "rb_cObject" => "Object",
+ "rb_cProc" => "Proc",
+ "rb_cRange" => "Range",
+ "rb_cRegexp" => "Regexp",
+ "rb_cRubyVM" => "RubyVM",
+ "rb_cSocket" => "Socket",
+ "rb_cString" => "String",
+ "rb_cStruct" => "Struct",
+ "rb_cSymbol" => "Symbol",
+ "rb_cThread" => "Thread",
+ "rb_cTime" => "Time",
+ "rb_cTrueClass" => "TrueClass",
+
+ "rb_eArgError" => "ArgError",
+ "rb_eEOFError" => "EOFError",
+ "rb_eException" => "Exception",
+ "rb_eFatal" => "fatal",
+ "rb_eFloatDomainError" => "FloatDomainError",
+ "rb_eIOError" => "IOError",
+ "rb_eIndexError" => "IndexError",
+ "rb_eInterrupt" => "Interrupt",
+ "rb_eLoadError" => "LoadError",
+ "rb_eNameError" => "NameError",
+ "rb_eNoMemError" => "NoMemError",
+ "rb_eNotImpError" => "NotImpError",
+ "rb_eRangeError" => "RangeError",
+ "rb_eRuntimeError" => "RuntimeError",
+ "rb_eScriptError" => "ScriptError",
+ "rb_eSecurityError" => "SecurityError",
+ "rb_eSignal" => "SignalException",
+ "rb_eStandardError" => "StandardError",
+ "rb_eSyntaxError" => "SyntaxError",
+ "rb_eSystemCallError" => "SystemCallError",
+ "rb_eSystemExit" => "SystemExit",
+ "rb_eTypeError" => "TypeError",
+ "rb_eZeroDivError" => "ZeroDivError",
+
+ "rb_mComparable" => "Comparable",
+ "rb_mEnumerable" => "Enumerable",
+ "rb_mErrno" => "Errno",
+ "rb_mFConst" => "File::Constants",
+ "rb_mFileTest" => "FileTest",
+ "rb_mGC" => "GC",
+ "rb_mKernel" => "Kernel",
+ "rb_mMath" => "Math",
+ "rb_mProcess" => "Process"
+ }
+
+end
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
new file mode 100644
index 0000000000..44dd50b0f7
--- /dev/null
+++ b/lib/rdoc/markdown.rb
@@ -0,0 +1,16286 @@
+# coding: UTF-8
+# :markup: markdown
+
+##
+# RDoc::Markdown as described by the [markdown syntax][syntax].
+#
+# To choose Markdown as your only default format see
+# RDoc::Options@Saved+Options for instructions on setting up a `.doc_options`
+# file to store your project default.
+#
+# ## Usage
+#
+# Here is a brief example of using this parse to read a markdown file by hand.
+#
+# data = File.read("README.md")
+# formatter = RDoc::Markup::ToHtml.new(RDoc::Options.new, nil)
+# html = RDoc::Markdown.parse(data).accept(formatter)
+#
+# # do something with html
+#
+# ## Extensions
+#
+# The following markdown extensions are supported by the parser, but not all
+# are used in RDoc output by default.
+#
+# ### RDoc
+#
+# The RDoc Markdown parser has the following built-in behaviors that cannot be
+# disabled.
+#
+# Underscores embedded in words are never interpreted as emphasis. (While the
+# [markdown dingus][dingus] emphasizes in-word underscores, neither the
+# Markdown syntax nor MarkdownTest mention this behavior.)
+#
+# For HTML output, RDoc always auto-links bare URLs.
+#
+# ### Break on Newline
+#
+# The break_on_newline extension converts all newlines into hard line breaks
+# as in [Github Flavored Markdown][GFM]. This extension is disabled by
+# default.
+#
+# ### CSS
+#
+# The #css extension enables CSS blocks to be included in the output, but they
+# are not used for any built-in RDoc output format. This extension is disabled
+# by default.
+#
+# Example:
+#
+# <style type="text/css">
+# h1 { font-size: 3em }
+# </style>
+#
+# ### Definition Lists
+#
+# The definition_lists extension allows definition lists using the [PHP
+# Markdown Extra syntax][PHPE], but only one label and definition are supported
+# at this time. This extension is enabled by default.
+#
+# Example:
+#
+# ```
+# cat
+# : A small furry mammal
+# that seems to sleep a lot
+#
+# ant
+# : A little insect that is known
+# to enjoy picnics
+#
+# ```
+#
+# Produces:
+#
+# cat
+# : A small furry mammal
+# that seems to sleep a lot
+#
+# ant
+# : A little insect that is known
+# to enjoy picnics
+#
+# ### Strike
+#
+# Example:
+#
+# ```
+# This is ~~striked~~.
+# ```
+#
+# Produces:
+#
+# This is ~~striked~~.
+#
+# ### Github
+#
+# The #github extension enables a partial set of [Github Flavored Markdown]
+# [GFM]. This extension is enabled by default.
+#
+# Supported github extensions include:
+#
+# #### Fenced code blocks
+#
+# Use ` ``` ` around a block of code instead of indenting it four spaces.
+#
+# #### Syntax highlighting
+#
+# Use ` ``` ruby ` as the start of a code fence to add syntax highlighting.
+# (Currently only `ruby` syntax is supported).
+#
+# ### HTML
+#
+# Enables raw HTML to be included in the output. This extension is enabled by
+# default.
+#
+# Example:
+#
+# <table>
+# ...
+# </table>
+#
+# ### Notes
+#
+# The #notes extension enables footnote support. This extension is enabled by
+# default.
+#
+# Example:
+#
+# Here is some text[^1] including an inline footnote ^[for short footnotes]
+#
+# ...
+#
+# [^1]: With the footnote text down at the bottom
+#
+# Produces:
+#
+# Here is some text[^1] including an inline footnote ^[for short footnotes]
+#
+# [^1]: With the footnote text down at the bottom
+#
+# ## Limitations
+#
+# * Link titles are not used
+# * Footnotes are collapsed into a single paragraph
+#
+# ## Author
+#
+# This markdown parser is a port to kpeg from [peg-markdown][pegmarkdown] by
+# John MacFarlane.
+#
+# It is used under the MIT license:
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# The port to kpeg was performed by Eric Hodel and Evan Phoenix
+#
+# [dingus]: http://daringfireball.net/projects/markdown/dingus
+# [GFM]: http://github.github.com/github-flavored-markdown/
+# [pegmarkdown]: https://github.com/jgm/peg-markdown
+# [PHPE]: http://michelf.com/projects/php-markdown/extra/#def-list
+# [syntax]: http://daringfireball.net/projects/markdown/syntax
+#--
+# Last updated to jgm/peg-markdown commit 8f8fc22ef0
+class RDoc::Markdown
+ # :stopdoc:
+
+ # This is distinct from setup_parser so that a standalone parser
+ # can redefine #initialize and still have access to the proper
+ # parser setup code.
+ def initialize(str, debug=false)
+ setup_parser(str, debug)
+ end
+
+
+
+ # Prepares for parsing +str+. If you define a custom initialize you must
+ # call this method before #parse
+ def setup_parser(str, debug=false)
+ set_string str, 0
+ @memoizations = Hash.new { |h,k| h[k] = {} }
+ @result = nil
+ @failed_rule = nil
+ @failing_rule_offset = -1
+
+ setup_foreign_grammar
+ end
+
+ attr_reader :string
+ attr_reader :failing_rule_offset
+ attr_accessor :result, :pos
+
+ def current_column(target=pos)
+ if c = string.rindex("\n", target-1)
+ return target - c - 1
+ end
+
+ target + 1
+ end
+
+ def current_line(target=pos)
+ cur_offset = 0
+ cur_line = 0
+
+ string.each_line do |line|
+ cur_line += 1
+ cur_offset += line.size
+ return cur_line if cur_offset >= target
+ end
+
+ -1
+ end
+
+ def lines
+ lines = []
+ string.each_line { |l| lines << l }
+ lines
+ end
+
+
+
+ def get_text(start)
+ @string[start..@pos-1]
+ end
+
+ # Sets the string and current parsing position for the parser.
+ def set_string string, pos
+ @string = string
+ @string_size = string ? string.size : 0
+ @pos = pos
+ end
+
+ def show_pos
+ width = 10
+ if @pos < width
+ "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
+ else
+ "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
+ end
+ end
+
+ def failure_info
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
+ else
+ "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
+ end
+ end
+
+ def failure_caret
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ line = lines[l-1]
+ "#{line}\n#{' ' * (c - 1)}^"
+ end
+
+ def failure_character
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+ lines[l-1][c-1, 1]
+ end
+
+ def failure_oneline
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ char = lines[l-1][c-1, 1]
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ else
+ "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ end
+ end
+
+ class ParseError < RuntimeError
+ end
+
+ def raise_error
+ raise ParseError, failure_oneline
+ end
+
+ def show_error(io=STDOUT)
+ error_pos = @failing_rule_offset
+ line_no = current_line(error_pos)
+ col_no = current_column(error_pos)
+
+ io.puts "On line #{line_no}, column #{col_no}:"
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
+ else
+ io.puts "Failed to match rule '#{@failed_rule}'"
+ end
+
+ io.puts "Got: #{string[error_pos,1].inspect}"
+ line = lines[line_no-1]
+ io.puts "=> #{line}"
+ io.print(" " * (col_no + 3))
+ io.puts "^"
+ end
+
+ def set_failed_rule(name)
+ if @pos > @failing_rule_offset
+ @failed_rule = name
+ @failing_rule_offset = @pos
+ end
+ end
+
+ attr_reader :failed_rule
+
+ def match_string(str)
+ len = str.size
+ if @string[pos,len] == str
+ @pos += len
+ return str
+ end
+
+ return nil
+ end
+
+ def scan(reg)
+ if m = reg.match(@string[@pos..-1])
+ width = m.end(0)
+ @pos += width
+ return true
+ end
+
+ return nil
+ end
+
+ if "".respond_to? :ord
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
+
+ s = @string[@pos].ord
+ @pos += 1
+ s
+ end
+ else
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
+
+ s = @string[@pos]
+ @pos += 1
+ s
+ end
+ end
+
+ def parse(rule=nil)
+ # We invoke the rules indirectly via apply
+ # instead of by just calling them as methods because
+ # if the rules use left recursion, apply needs to
+ # manage that.
+
+ if !rule
+ apply(:_root)
+ else
+ method = rule.gsub("-","_hyphen_")
+ apply :"_#{method}"
+ end
+ end
+
+ class MemoEntry
+ def initialize(ans, pos)
+ @ans = ans
+ @pos = pos
+ @result = nil
+ @set = false
+ @left_rec = false
+ end
+
+ attr_reader :ans, :pos, :result, :set
+ attr_accessor :left_rec
+
+ def move!(ans, pos, result)
+ @ans = ans
+ @pos = pos
+ @result = result
+ @set = true
+ @left_rec = false
+ end
+ end
+
+ def external_invoke(other, rule, *args)
+ old_pos = @pos
+ old_string = @string
+
+ set_string other.string, other.pos
+
+ begin
+ if val = __send__(rule, *args)
+ other.pos = @pos
+ other.result = @result
+ else
+ other.set_failed_rule "#{self.class}##{rule}"
+ end
+ val
+ ensure
+ set_string old_string, old_pos
+ end
+ end
+
+ def apply_with_args(rule, *args)
+ memo_key = [rule, args]
+ if m = @memoizations[memo_key][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[memo_key][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule, *args
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, args, start_pos, m)
+ else
+ return ans
+ end
+ end
+ end
+
+ def apply(rule)
+ if m = @memoizations[rule][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[rule][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, nil, start_pos, m)
+ else
+ return ans
+ end
+ end
+ end
+
+ def grow_lr(rule, args, start_pos, m)
+ while true
+ @pos = start_pos
+ @result = m.result
+
+ if args
+ ans = __send__ rule, *args
+ else
+ ans = __send__ rule
+ end
+ return nil unless ans
+
+ break if @pos <= m.pos
+
+ m.move! ans, @pos, @result
+ end
+
+ @result = m.result
+ @pos = m.pos
+ return m.ans
+ end
+
+ class RuleInfo
+ def initialize(name, rendered)
+ @name = name
+ @rendered = rendered
+ end
+
+ attr_reader :name, :rendered
+ end
+
+ def self.rule_info(name, rendered)
+ RuleInfo.new(name, rendered)
+ end
+
+
+ # :startdoc:
+
+
+
+ require 'rdoc'
+ require 'rdoc/markup/to_joined_paragraph'
+ require 'rdoc/markdown/entities'
+
+ require 'rdoc/markdown/literals'
+
+ ##
+ # Supported extensions
+
+ EXTENSIONS = []
+
+ ##
+ # Extensions enabled by default
+
+ DEFAULT_EXTENSIONS = [
+ :definition_lists,
+ :github,
+ :html,
+ :notes,
+ :strike,
+ ]
+
+ # :section: Extensions
+
+ ##
+ # Creates extension methods for the `name` extension to enable and disable
+ # the extension and to query if they are active.
+
+ def self.extension name
+ EXTENSIONS << name
+
+ define_method "#{name}?" do
+ extension? name
+ end
+
+ define_method "#{name}=" do |enable|
+ extension name, enable
+ end
+ end
+
+ ##
+ # Converts all newlines into hard breaks
+
+ extension :break_on_newline
+
+ ##
+ # Allow style blocks
+
+ extension :css
+
+ ##
+ # Allow PHP Markdown Extras style definition lists
+
+ extension :definition_lists
+
+ ##
+ # Allow Github Flavored Markdown
+
+ extension :github
+
+ ##
+ # Allow HTML
+
+ extension :html
+
+ ##
+ # Enables the notes extension
+
+ extension :notes
+
+ ##
+ # Enables the strike extension
+
+ extension :strike
+
+ # :section:
+
+ ##
+ # Parses the `markdown` document into an RDoc::Document using the default
+ # extensions.
+
+ def self.parse markdown
+ parser = new
+
+ parser.parse markdown
+ end
+
+ # TODO remove when kpeg 0.10 is released
+ alias orig_initialize initialize # :nodoc:
+
+ ##
+ # Creates a new markdown parser that enables the given +extensions+.
+
+ def initialize extensions = DEFAULT_EXTENSIONS, debug = false
+ @debug = debug
+ @formatter = RDoc::Markup::ToJoinedParagraph.new
+ @extensions = extensions
+
+ @references = nil
+ @unlinked_references = nil
+
+ @footnotes = nil
+ @note_order = nil
+ end
+
+ ##
+ # Wraps `text` in emphasis for rdoc inline formatting
+
+ def emphasis text
+ if text =~ /\A[a-z\d.\/]+\z/i then
+ "_#{text}_"
+ else
+ "<em>#{text}</em>"
+ end
+ end
+
+ ##
+ # :category: Extensions
+ #
+ # Is the extension `name` enabled?
+
+ def extension? name
+ @extensions.include? name
+ end
+
+ ##
+ # :category: Extensions
+ #
+ # Enables or disables the extension with `name`
+
+ def extension name, enable
+ if enable then
+ @extensions |= [name]
+ else
+ @extensions -= [name]
+ end
+ end
+
+ ##
+ # Parses `text` in a clone of this parser. This is used for handling nested
+ # lists the same way as markdown_parser.
+
+ def inner_parse text # :nodoc:
+ parser = clone
+
+ parser.setup_parser text, @debug
+
+ parser.peg_parse
+
+ doc = parser.result
+
+ doc.accept @formatter
+
+ doc.parts
+ end
+
+ ##
+ # Finds a link reference for `label` and creates a new link to it with
+ # `content` as the link text. If `label` was not encountered in the
+ # reference-gathering parser pass the label and content are reconstructed
+ # with the linking `text` (usually whitespace).
+
+ def link_to content, label = content, text = nil
+ raise ParseError, 'enable notes extension' if
+ content.start_with? '^' and label.equal? content
+
+ if ref = @references[label] then
+ "{#{content}}[#{ref}]"
+ elsif label.equal? content then
+ "[#{content}]#{text}"
+ else
+ "[#{content}]#{text}[#{label}]"
+ end
+ end
+
+ ##
+ # Creates an RDoc::Markup::ListItem by parsing the `unparsed` content from
+ # the first parsing pass.
+
+ def list_item_from unparsed
+ parsed = inner_parse unparsed.join
+ RDoc::Markup::ListItem.new nil, *parsed
+ end
+
+ ##
+ # Stores `label` as a note and fills in previously unknown note references.
+
+ def note label
+ #foottext = "rdoc-label:foottext-#{label}:footmark-#{label}"
+
+ #ref.replace foottext if ref = @unlinked_notes.delete(label)
+
+ @notes[label] = foottext
+
+ #"{^1}[rdoc-label:footmark-#{label}:foottext-#{label}] "
+ end
+
+ ##
+ # Creates a new link for the footnote `reference` and adds the reference to
+ # the note order list for proper display at the end of the document.
+
+ def note_for ref
+ @note_order << ref
+
+ label = @note_order.length
+
+ "{*#{label}}[rdoc-label:foottext-#{label}:footmark-#{label}]"
+ end
+
+ ##
+ # The internal kpeg parse method
+
+ alias peg_parse parse # :nodoc:
+
+ ##
+ # Creates an RDoc::Markup::Paragraph from `parts` and including
+ # extension-specific behavior
+
+ def paragraph parts
+ parts = parts.map do |part|
+ if "\n" == part then
+ RDoc::Markup::HardBreak.new
+ else
+ part
+ end
+ end if break_on_newline?
+
+ RDoc::Markup::Paragraph.new(*parts)
+ end
+
+ ##
+ # Parses `markdown` into an RDoc::Document
+
+ def parse markdown
+ @references = {}
+ @unlinked_references = {}
+
+ markdown += "\n\n"
+
+ setup_parser markdown, @debug
+ peg_parse 'References'
+
+ if notes? then
+ @footnotes = {}
+
+ setup_parser markdown, @debug
+ peg_parse 'Notes'
+
+ # using note_order on the first pass would be a bug
+ @note_order = []
+ end
+
+ setup_parser markdown, @debug
+ peg_parse
+
+ doc = result
+
+ if notes? and not @footnotes.empty? then
+ doc << RDoc::Markup::Rule.new(1)
+
+ @note_order.each_with_index do |ref, index|
+ label = index + 1
+ note = @footnotes[ref]
+
+ link = "{^#{label}}[rdoc-label:footmark-#{label}:foottext-#{label}] "
+ note.parts.unshift link
+
+ doc << note
+ end
+ end
+
+ doc.accept @formatter
+
+ doc
+ end
+
+ ##
+ # Stores `label` as a reference to `link` and fills in previously unknown
+ # link references.
+
+ def reference label, link
+ if ref = @unlinked_references.delete(label) then
+ ref.replace link
+ end
+
+ @references[label] = link
+ end
+
+ ##
+ # Wraps `text` in strong markup for rdoc inline formatting
+
+ def strong text
+ if text =~ /\A[a-z\d.\/-]+\z/i then
+ "*#{text}*"
+ else
+ "<b>#{text}</b>"
+ end
+ end
+
+ ##
+ # Wraps `text` in strike markup for rdoc inline formatting
+
+ def strike text
+ if text =~ /\A[a-z\d.\/-]+\z/i then
+ "~#{text}~"
+ else
+ "<s>#{text}</s>"
+ end
+ end
+
+
+ # :stopdoc:
+ def setup_foreign_grammar
+ @_grammar_literals = RDoc::Markdown::Literals.new(nil)
+ end
+
+ # root = Doc
+ def _root
+ _tmp = apply(:_Doc)
+ set_failed_rule :_root unless _tmp
+ return _tmp
+ end
+
+ # Doc = BOM? Block*:a { RDoc::Markup::Document.new(*a.compact) }
+ def _Doc
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = apply(:_BOM)
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _ary = []
+ while true
+ _tmp = apply(:_Block)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Document.new(*a.compact) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Doc unless _tmp
+ return _tmp
+ end
+
+ # Block = @BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)
+ def _Block
+
+ _save = self.pos
+ while true # sequence
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_BlockQuote)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Verbatim)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_CodeFence)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Note)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Reference)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_HorizontalRule)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Heading)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_OrderedList)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_BulletList)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_DefinitionList)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_HtmlBlock)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_StyleBlock)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Para)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_Plain)
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Block unless _tmp
+ return _tmp
+ end
+
+ # Para = @NonindentSpace Inlines:a @BlankLine+ { paragraph a }
+ def _Para
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Inlines)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; paragraph a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Para unless _tmp
+ return _tmp
+ end
+
+ # Plain = Inlines:a { paragraph a }
+ def _Plain
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Inlines)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; paragraph a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Plain unless _tmp
+ return _tmp
+ end
+
+ # AtxInline = !@Newline !(@Sp /#*/ @Sp @Newline) Inline
+ def _AtxInline
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = scan(/\A(?-mix:#*)/)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save2
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Inline)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_AtxInline unless _tmp
+ return _tmp
+ end
+
+ # AtxStart = < /\#{1,6}/ > { text.length }
+ def _AtxStart
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:\#{1,6})/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text.length ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_AtxStart unless _tmp
+ return _tmp
+ end
+
+ # AtxHeading = AtxStart:s @Sp AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }
+ def _AtxHeading
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_AtxStart)
+ s = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_AtxInline)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_AtxInline)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = scan(/\A(?-mix:#*)/)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ unless _tmp
+ _tmp = true
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Heading.new(s, a.join) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_AtxHeading unless _tmp
+ return _tmp
+ end
+
+ # SetextHeading = (SetextHeading1 | SetextHeading2)
+ def _SetextHeading
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_SetextHeading1)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_SetextHeading2)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_SetextHeading unless _tmp
+ return _tmp
+ end
+
+ # SetextBottom1 = /={1,}/ @Newline
+ def _SetextBottom1
+
+ _save = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?-mix:={1,})/)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_SetextBottom1 unless _tmp
+ return _tmp
+ end
+
+ # SetextBottom2 = /-{1,}/ @Newline
+ def _SetextBottom2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?-mix:-{1,})/)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_SetextBottom2 unless _tmp
+ return _tmp
+ end
+
+ # SetextHeading1 = &(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }
+ def _SetextHeading1
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = _RawLine()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = apply(:_SetextBottom1)
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_SetextBottom1)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Heading.new(1, a.join) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_SetextHeading1 unless _tmp
+ return _tmp
+ end
+
+ # SetextHeading2 = &(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }
+ def _SetextHeading2
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = _RawLine()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = apply(:_SetextBottom2)
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_SetextBottom2)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Heading.new(2, a.join) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_SetextHeading2 unless _tmp
+ return _tmp
+ end
+
+ # Heading = (SetextHeading | AtxHeading)
+ def _Heading
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_SetextHeading)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_AtxHeading)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Heading unless _tmp
+ return _tmp
+ end
+
+ # BlockQuote = BlockQuoteRaw:a { RDoc::Markup::BlockQuote.new(*a) }
+ def _BlockQuote
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_BlockQuoteRaw)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::BlockQuote.new(*a) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_BlockQuote unless _tmp
+ return _tmp
+ end
+
+ # BlockQuoteRaw = @StartList:a (">" " "? Line:l { a << l } (!">" !@BlankLine Line:c { a << c })* (@BlankLine:n { a << n })*)+ { inner_parse a.join }
+ def _BlockQuoteRaw
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save3 = self.pos
+ _tmp = match_string(" ")
+ unless _tmp
+ _tmp = true
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = apply(:_Line)
+ l = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _save7 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = apply(:_Line)
+ c = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << c ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ while true
+
+ _save9 = self.pos
+ while true # sequence
+ _tmp = _BlankLine()
+ n = @result
+ unless _tmp
+ self.pos = _save9
+ break
+ end
+ @result = begin; a << n ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save9
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _save11 = self.pos
+ _tmp = match_string(" ")
+ unless _tmp
+ _tmp = true
+ self.pos = _save11
+ end
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _tmp = apply(:_Line)
+ l = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ while true
+
+ _save13 = self.pos
+ while true # sequence
+ _save14 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save14
+ unless _tmp
+ self.pos = _save13
+ break
+ end
+ _save15 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save15
+ unless _tmp
+ self.pos = _save13
+ break
+ end
+ _tmp = apply(:_Line)
+ c = @result
+ unless _tmp
+ self.pos = _save13
+ break
+ end
+ @result = begin; a << c ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save13
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ while true
+
+ _save17 = self.pos
+ while true # sequence
+ _tmp = _BlankLine()
+ n = @result
+ unless _tmp
+ self.pos = _save17
+ break
+ end
+ @result = begin; a << n ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save17
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; inner_parse a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_BlockQuoteRaw unless _tmp
+ return _tmp
+ end
+
+ # NonblankIndentedLine = !@BlankLine IndentedLine
+ def _NonblankIndentedLine
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_IndentedLine)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_NonblankIndentedLine unless _tmp
+ return _tmp
+ end
+
+ # VerbatimChunk = @BlankLine*:a NonblankIndentedLine+:b { a.concat b }
+ def _VerbatimChunk
+
+ _save = self.pos
+ while true # sequence
+ _ary = []
+ while true
+ _tmp = _BlankLine()
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _ary = []
+ _tmp = apply(:_NonblankIndentedLine)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_NonblankIndentedLine)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save2
+ end
+ b = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a.concat b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_VerbatimChunk unless _tmp
+ return _tmp
+ end
+
+ # Verbatim = VerbatimChunk+:a { RDoc::Markup::Verbatim.new(*a.flatten) }
+ def _Verbatim
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_VerbatimChunk)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_VerbatimChunk)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Verbatim.new(*a.flatten) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Verbatim unless _tmp
+ return _tmp
+ end
+
+ # HorizontalRule = @NonindentSpace ("*" @Sp "*" @Sp "*" (@Sp "*")* | "-" @Sp "-" @Sp "-" (@Sp "-")* | "_" @Sp "_" @Sp "_" (@Sp "_")*) @Sp @Newline @BlankLine+ { RDoc::Markup::Rule.new 1 }
+ def _HorizontalRule
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ while true
+
+ _save4 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string("-")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = match_string("-")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = match_string("-")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ while true
+
+ _save7 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = match_string("-")
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save8 = self.pos
+ while true # sequence
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ while true
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save11 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save11
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::Rule.new 1 ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HorizontalRule unless _tmp
+ return _tmp
+ end
+
+ # Bullet = !HorizontalRule @NonindentSpace /[+*-]/ @Spacechar+
+ def _Bullet
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = apply(:_HorizontalRule)
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = scan(/\A(?-mix:[+*-])/)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = _Spacechar()
+ if _tmp
+ while true
+ _tmp = _Spacechar()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Bullet unless _tmp
+ return _tmp
+ end
+
+ # BulletList = &Bullet (ListTight | ListLoose):a { RDoc::Markup::List.new(:BULLET, *a) }
+ def _BulletList
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = apply(:_Bullet)
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_ListTight)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_ListLoose)
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::List.new(:BULLET, *a) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_BulletList unless _tmp
+ return _tmp
+ end
+
+ # ListTight = ListItemTight+:a @BlankLine* !(Bullet | Enumerator) { a }
+ def _ListTight
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_ListItemTight)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_ListItemTight)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # choice
+ _tmp = apply(:_Bullet)
+ break if _tmp
+ self.pos = _save4
+ _tmp = apply(:_Enumerator)
+ break if _tmp
+ self.pos = _save4
+ break
+ end # end choice
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListTight unless _tmp
+ return _tmp
+ end
+
+ # ListLoose = @StartList:a (ListItem:b @BlankLine* { a << b })+ { a }
+ def _ListLoose
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = apply(:_ListItem)
+ b = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save4 = self.pos
+ while true # sequence
+ _tmp = apply(:_ListItem)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListLoose unless _tmp
+ return _tmp
+ end
+
+ # ListItem = (Bullet | Enumerator) @StartList:a ListBlock:b { a << b } (ListContinuationBlock:c { a.push(*c) })* { list_item_from a }
+ def _ListItem
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_Bullet)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_Enumerator)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_ListBlock)
+ b = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = apply(:_ListContinuationBlock)
+ c = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a.push(*c) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; list_item_from a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListItem unless _tmp
+ return _tmp
+ end
+
+ # ListItemTight = (Bullet | Enumerator) ListBlock:a (!@BlankLine ListContinuationBlock:b { a.push(*b) })* !ListContinuationBlock { list_item_from a }
+ def _ListItemTight
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_Bullet)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_Enumerator)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_ListBlock)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_ListContinuationBlock)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a.push(*b) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save5 = self.pos
+ _tmp = apply(:_ListContinuationBlock)
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; list_item_from a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListItemTight unless _tmp
+ return _tmp
+ end
+
+ # ListBlock = !@BlankLine Line:a ListBlockLine*:c { [a, *c] }
+ def _ListBlock
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Line)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _ary = []
+ while true
+ _tmp = apply(:_ListBlockLine)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ c = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; [a, *c] ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListBlock unless _tmp
+ return _tmp
+ end
+
+ # ListContinuationBlock = @StartList:a @BlankLine* { a << "\n" } (Indent ListBlock:b { a.concat b })+ { a }
+ def _ListContinuationBlock
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a << "\n" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = apply(:_Indent)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_ListBlock)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a.concat b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save4 = self.pos
+ while true # sequence
+ _tmp = apply(:_Indent)
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_ListBlock)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a.concat b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListContinuationBlock unless _tmp
+ return _tmp
+ end
+
+ # Enumerator = @NonindentSpace [0-9]+ "." @Spacechar+
+ def _Enumerator
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _save2 = self.pos
+ _tmp = get_byte
+ if _tmp
+ unless _tmp >= 48 and _tmp <= 57
+ self.pos = _save2
+ _tmp = nil
+ end
+ end
+ if _tmp
+ while true
+ _save3 = self.pos
+ _tmp = get_byte
+ if _tmp
+ unless _tmp >= 48 and _tmp <= 57
+ self.pos = _save3
+ _tmp = nil
+ end
+ end
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(".")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save4 = self.pos
+ _tmp = _Spacechar()
+ if _tmp
+ while true
+ _tmp = _Spacechar()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save4
+ end
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Enumerator unless _tmp
+ return _tmp
+ end
+
+ # OrderedList = &Enumerator (ListTight | ListLoose):a { RDoc::Markup::List.new(:NUMBER, *a) }
+ def _OrderedList
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = apply(:_Enumerator)
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_ListTight)
+ break if _tmp
+ self.pos = _save2
+ _tmp = apply(:_ListLoose)
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::List.new(:NUMBER, *a) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_OrderedList unless _tmp
+ return _tmp
+ end
+
+ # ListBlockLine = !@BlankLine !(Indent? (Bullet | Enumerator)) !HorizontalRule OptionallyIndentedLine
+ def _ListBlockLine
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_Indent)
+ unless _tmp
+ _tmp = true
+ self.pos = _save4
+ end
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+
+ _save5 = self.pos
+ while true # choice
+ _tmp = apply(:_Bullet)
+ break if _tmp
+ self.pos = _save5
+ _tmp = apply(:_Enumerator)
+ break if _tmp
+ self.pos = _save5
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save2
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save6 = self.pos
+ _tmp = apply(:_HorizontalRule)
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_OptionallyIndentedLine)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ListBlockLine unless _tmp
+ return _tmp
+ end
+
+ # HtmlOpenAnchor = "<" Spnl ("a" | "A") Spnl HtmlAttribute* ">"
+ def _HtmlOpenAnchor
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("a")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("A")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlOpenAnchor unless _tmp
+ return _tmp
+ end
+
+ # HtmlCloseAnchor = "<" Spnl "/" ("a" | "A") Spnl ">"
+ def _HtmlCloseAnchor
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("a")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("A")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlCloseAnchor unless _tmp
+ return _tmp
+ end
+
+ # HtmlAnchor = HtmlOpenAnchor (HtmlAnchor | !HtmlCloseAnchor .)* HtmlCloseAnchor
+ def _HtmlAnchor
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlOpenAnchor)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlAnchor)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlCloseAnchor)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlCloseAnchor)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlAnchor unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenAddress = "<" Spnl ("address" | "ADDRESS") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenAddress
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("address")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("ADDRESS")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenAddress unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseAddress = "<" Spnl "/" ("address" | "ADDRESS") Spnl ">"
+ def _HtmlBlockCloseAddress
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("address")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("ADDRESS")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseAddress unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockAddress = HtmlBlockOpenAddress (HtmlBlockAddress | !HtmlBlockCloseAddress .)* HtmlBlockCloseAddress
+ def _HtmlBlockAddress
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenAddress)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockAddress)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseAddress)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseAddress)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockAddress unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenBlockquote = "<" Spnl ("blockquote" | "BLOCKQUOTE") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenBlockquote
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("blockquote")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("BLOCKQUOTE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenBlockquote unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseBlockquote = "<" Spnl "/" ("blockquote" | "BLOCKQUOTE") Spnl ">"
+ def _HtmlBlockCloseBlockquote
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("blockquote")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("BLOCKQUOTE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseBlockquote unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockBlockquote = HtmlBlockOpenBlockquote (HtmlBlockBlockquote | !HtmlBlockCloseBlockquote .)* HtmlBlockCloseBlockquote
+ def _HtmlBlockBlockquote
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenBlockquote)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockBlockquote)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseBlockquote)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseBlockquote)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockBlockquote unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenCenter = "<" Spnl ("center" | "CENTER") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenCenter
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("center")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("CENTER")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenCenter unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseCenter = "<" Spnl "/" ("center" | "CENTER") Spnl ">"
+ def _HtmlBlockCloseCenter
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("center")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("CENTER")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseCenter unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCenter = HtmlBlockOpenCenter (HtmlBlockCenter | !HtmlBlockCloseCenter .)* HtmlBlockCloseCenter
+ def _HtmlBlockCenter
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenCenter)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockCenter)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseCenter)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseCenter)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCenter unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenDir = "<" Spnl ("dir" | "DIR") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenDir
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dir")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DIR")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenDir unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseDir = "<" Spnl "/" ("dir" | "DIR") Spnl ">"
+ def _HtmlBlockCloseDir
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dir")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DIR")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseDir unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockDir = HtmlBlockOpenDir (HtmlBlockDir | !HtmlBlockCloseDir .)* HtmlBlockCloseDir
+ def _HtmlBlockDir
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenDir)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockDir)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseDir)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseDir)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockDir unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenDiv = "<" Spnl ("div" | "DIV") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenDiv
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("div")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DIV")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenDiv unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseDiv = "<" Spnl "/" ("div" | "DIV") Spnl ">"
+ def _HtmlBlockCloseDiv
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("div")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DIV")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseDiv unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockDiv = HtmlBlockOpenDiv (HtmlBlockDiv | !HtmlBlockCloseDiv .)* HtmlBlockCloseDiv
+ def _HtmlBlockDiv
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenDiv)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockDiv)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseDiv)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseDiv)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockDiv unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenDl = "<" Spnl ("dl" | "DL") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenDl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dl")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenDl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseDl = "<" Spnl "/" ("dl" | "DL") Spnl ">"
+ def _HtmlBlockCloseDl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dl")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseDl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockDl = HtmlBlockOpenDl (HtmlBlockDl | !HtmlBlockCloseDl .)* HtmlBlockCloseDl
+ def _HtmlBlockDl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenDl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockDl)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseDl)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseDl)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockDl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenFieldset = "<" Spnl ("fieldset" | "FIELDSET") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenFieldset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("fieldset")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FIELDSET")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenFieldset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseFieldset = "<" Spnl "/" ("fieldset" | "FIELDSET") Spnl ">"
+ def _HtmlBlockCloseFieldset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("fieldset")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FIELDSET")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseFieldset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockFieldset = HtmlBlockOpenFieldset (HtmlBlockFieldset | !HtmlBlockCloseFieldset .)* HtmlBlockCloseFieldset
+ def _HtmlBlockFieldset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenFieldset)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockFieldset)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseFieldset)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseFieldset)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockFieldset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenForm = "<" Spnl ("form" | "FORM") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenForm
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("form")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FORM")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenForm unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseForm = "<" Spnl "/" ("form" | "FORM") Spnl ">"
+ def _HtmlBlockCloseForm
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("form")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FORM")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseForm unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockForm = HtmlBlockOpenForm (HtmlBlockForm | !HtmlBlockCloseForm .)* HtmlBlockCloseForm
+ def _HtmlBlockForm
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenForm)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockForm)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseForm)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseForm)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockForm unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH1 = "<" Spnl ("h1" | "H1") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH1
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h1")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H1")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH1 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH1 = "<" Spnl "/" ("h1" | "H1") Spnl ">"
+ def _HtmlBlockCloseH1
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h1")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H1")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH1 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH1 = HtmlBlockOpenH1 (HtmlBlockH1 | !HtmlBlockCloseH1 .)* HtmlBlockCloseH1
+ def _HtmlBlockH1
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH1)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH1)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH1)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH1)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH1 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH2 = "<" Spnl ("h2" | "H2") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h2")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H2")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH2 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH2 = "<" Spnl "/" ("h2" | "H2") Spnl ">"
+ def _HtmlBlockCloseH2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h2")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H2")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH2 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH2 = HtmlBlockOpenH2 (HtmlBlockH2 | !HtmlBlockCloseH2 .)* HtmlBlockCloseH2
+ def _HtmlBlockH2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH2)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH2)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH2)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH2)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH2 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH3 = "<" Spnl ("h3" | "H3") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH3
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h3")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H3")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH3 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH3 = "<" Spnl "/" ("h3" | "H3") Spnl ">"
+ def _HtmlBlockCloseH3
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h3")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H3")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH3 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH3 = HtmlBlockOpenH3 (HtmlBlockH3 | !HtmlBlockCloseH3 .)* HtmlBlockCloseH3
+ def _HtmlBlockH3
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH3)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH3)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH3)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH3)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH3 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH4 = "<" Spnl ("h4" | "H4") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH4
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h4")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H4")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH4 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH4 = "<" Spnl "/" ("h4" | "H4") Spnl ">"
+ def _HtmlBlockCloseH4
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h4")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H4")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH4 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH4 = HtmlBlockOpenH4 (HtmlBlockH4 | !HtmlBlockCloseH4 .)* HtmlBlockCloseH4
+ def _HtmlBlockH4
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH4)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH4)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH4)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH4)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH4 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH5 = "<" Spnl ("h5" | "H5") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH5
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h5")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H5")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH5 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH5 = "<" Spnl "/" ("h5" | "H5") Spnl ">"
+ def _HtmlBlockCloseH5
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h5")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H5")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH5 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH5 = HtmlBlockOpenH5 (HtmlBlockH5 | !HtmlBlockCloseH5 .)* HtmlBlockCloseH5
+ def _HtmlBlockH5
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH5)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH5)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH5)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH5)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH5 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenH6 = "<" Spnl ("h6" | "H6") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenH6
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h6")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H6")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenH6 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseH6 = "<" Spnl "/" ("h6" | "H6") Spnl ">"
+ def _HtmlBlockCloseH6
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("h6")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("H6")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseH6 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockH6 = HtmlBlockOpenH6 (HtmlBlockH6 | !HtmlBlockCloseH6 .)* HtmlBlockCloseH6
+ def _HtmlBlockH6
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenH6)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockH6)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseH6)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseH6)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockH6 unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenMenu = "<" Spnl ("menu" | "MENU") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenMenu
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("menu")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("MENU")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenMenu unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseMenu = "<" Spnl "/" ("menu" | "MENU") Spnl ">"
+ def _HtmlBlockCloseMenu
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("menu")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("MENU")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseMenu unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockMenu = HtmlBlockOpenMenu (HtmlBlockMenu | !HtmlBlockCloseMenu .)* HtmlBlockCloseMenu
+ def _HtmlBlockMenu
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenMenu)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockMenu)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseMenu)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseMenu)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockMenu unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenNoframes = "<" Spnl ("noframes" | "NOFRAMES") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenNoframes
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("noframes")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("NOFRAMES")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenNoframes unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseNoframes = "<" Spnl "/" ("noframes" | "NOFRAMES") Spnl ">"
+ def _HtmlBlockCloseNoframes
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("noframes")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("NOFRAMES")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseNoframes unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockNoframes = HtmlBlockOpenNoframes (HtmlBlockNoframes | !HtmlBlockCloseNoframes .)* HtmlBlockCloseNoframes
+ def _HtmlBlockNoframes
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenNoframes)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockNoframes)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseNoframes)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseNoframes)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockNoframes unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenNoscript = "<" Spnl ("noscript" | "NOSCRIPT") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenNoscript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("noscript")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("NOSCRIPT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenNoscript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseNoscript = "<" Spnl "/" ("noscript" | "NOSCRIPT") Spnl ">"
+ def _HtmlBlockCloseNoscript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("noscript")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("NOSCRIPT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseNoscript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockNoscript = HtmlBlockOpenNoscript (HtmlBlockNoscript | !HtmlBlockCloseNoscript .)* HtmlBlockCloseNoscript
+ def _HtmlBlockNoscript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenNoscript)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockNoscript)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseNoscript)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseNoscript)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockNoscript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenOl = "<" Spnl ("ol" | "OL") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenOl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("ol")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("OL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenOl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseOl = "<" Spnl "/" ("ol" | "OL") Spnl ">"
+ def _HtmlBlockCloseOl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("ol")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("OL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseOl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOl = HtmlBlockOpenOl (HtmlBlockOl | !HtmlBlockCloseOl .)* HtmlBlockCloseOl
+ def _HtmlBlockOl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenOl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockOl)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseOl)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseOl)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenP = "<" Spnl ("p" | "P") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenP
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("p")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("P")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenP unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseP = "<" Spnl "/" ("p" | "P") Spnl ">"
+ def _HtmlBlockCloseP
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("p")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("P")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseP unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockP = HtmlBlockOpenP (HtmlBlockP | !HtmlBlockCloseP .)* HtmlBlockCloseP
+ def _HtmlBlockP
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenP)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockP)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseP)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseP)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockP unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenPre = "<" Spnl ("pre" | "PRE") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenPre
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("pre")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("PRE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenPre unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockClosePre = "<" Spnl "/" ("pre" | "PRE") Spnl ">"
+ def _HtmlBlockClosePre
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("pre")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("PRE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockClosePre unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockPre = HtmlBlockOpenPre (HtmlBlockPre | !HtmlBlockClosePre .)* HtmlBlockClosePre
+ def _HtmlBlockPre
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenPre)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockPre)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockClosePre)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockClosePre)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockPre unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTable = "<" Spnl ("table" | "TABLE") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTable
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("table")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TABLE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTable unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTable = "<" Spnl "/" ("table" | "TABLE") Spnl ">"
+ def _HtmlBlockCloseTable
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("table")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TABLE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTable unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTable = HtmlBlockOpenTable (HtmlBlockTable | !HtmlBlockCloseTable .)* HtmlBlockCloseTable
+ def _HtmlBlockTable
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTable)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTable)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTable)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTable)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTable unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenUl = "<" Spnl ("ul" | "UL") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenUl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("ul")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("UL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenUl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseUl = "<" Spnl "/" ("ul" | "UL") Spnl ">"
+ def _HtmlBlockCloseUl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("ul")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("UL")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseUl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockUl = HtmlBlockOpenUl (HtmlBlockUl | !HtmlBlockCloseUl .)* HtmlBlockCloseUl
+ def _HtmlBlockUl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenUl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockUl)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseUl)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseUl)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockUl unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenDd = "<" Spnl ("dd" | "DD") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenDd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dd")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenDd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseDd = "<" Spnl "/" ("dd" | "DD") Spnl ">"
+ def _HtmlBlockCloseDd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dd")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseDd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockDd = HtmlBlockOpenDd (HtmlBlockDd | !HtmlBlockCloseDd .)* HtmlBlockCloseDd
+ def _HtmlBlockDd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenDd)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockDd)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseDd)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseDd)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockDd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenDt = "<" Spnl ("dt" | "DT") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenDt
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dt")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenDt unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseDt = "<" Spnl "/" ("dt" | "DT") Spnl ">"
+ def _HtmlBlockCloseDt
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("dt")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("DT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseDt unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockDt = HtmlBlockOpenDt (HtmlBlockDt | !HtmlBlockCloseDt .)* HtmlBlockCloseDt
+ def _HtmlBlockDt
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenDt)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockDt)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseDt)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseDt)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockDt unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenFrameset = "<" Spnl ("frameset" | "FRAMESET") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenFrameset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("frameset")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FRAMESET")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenFrameset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseFrameset = "<" Spnl "/" ("frameset" | "FRAMESET") Spnl ">"
+ def _HtmlBlockCloseFrameset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("frameset")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("FRAMESET")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseFrameset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockFrameset = HtmlBlockOpenFrameset (HtmlBlockFrameset | !HtmlBlockCloseFrameset .)* HtmlBlockCloseFrameset
+ def _HtmlBlockFrameset
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenFrameset)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockFrameset)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseFrameset)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseFrameset)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockFrameset unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenLi = "<" Spnl ("li" | "LI") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenLi
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("li")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("LI")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenLi unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseLi = "<" Spnl "/" ("li" | "LI") Spnl ">"
+ def _HtmlBlockCloseLi
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("li")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("LI")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseLi unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockLi = HtmlBlockOpenLi (HtmlBlockLi | !HtmlBlockCloseLi .)* HtmlBlockCloseLi
+ def _HtmlBlockLi
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenLi)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockLi)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseLi)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseLi)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockLi unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTbody = "<" Spnl ("tbody" | "TBODY") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTbody
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tbody")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TBODY")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTbody unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTbody = "<" Spnl "/" ("tbody" | "TBODY") Spnl ">"
+ def _HtmlBlockCloseTbody
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tbody")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TBODY")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTbody unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTbody = HtmlBlockOpenTbody (HtmlBlockTbody | !HtmlBlockCloseTbody .)* HtmlBlockCloseTbody
+ def _HtmlBlockTbody
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTbody)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTbody)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTbody)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTbody)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTbody unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTd = "<" Spnl ("td" | "TD") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("td")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTd = "<" Spnl "/" ("td" | "TD") Spnl ">"
+ def _HtmlBlockCloseTd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("td")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTd = HtmlBlockOpenTd (HtmlBlockTd | !HtmlBlockCloseTd .)* HtmlBlockCloseTd
+ def _HtmlBlockTd
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTd)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTd)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTd)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTd)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTd unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTfoot = "<" Spnl ("tfoot" | "TFOOT") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTfoot
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tfoot")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TFOOT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTfoot unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTfoot = "<" Spnl "/" ("tfoot" | "TFOOT") Spnl ">"
+ def _HtmlBlockCloseTfoot
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tfoot")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TFOOT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTfoot unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTfoot = HtmlBlockOpenTfoot (HtmlBlockTfoot | !HtmlBlockCloseTfoot .)* HtmlBlockCloseTfoot
+ def _HtmlBlockTfoot
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTfoot)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTfoot)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTfoot)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTfoot)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTfoot unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTh = "<" Spnl ("th" | "TH") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTh
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("th")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TH")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTh unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTh = "<" Spnl "/" ("th" | "TH") Spnl ">"
+ def _HtmlBlockCloseTh
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("th")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TH")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTh unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTh = HtmlBlockOpenTh (HtmlBlockTh | !HtmlBlockCloseTh .)* HtmlBlockCloseTh
+ def _HtmlBlockTh
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTh)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTh)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTh)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTh)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTh unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenThead = "<" Spnl ("thead" | "THEAD") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenThead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("thead")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("THEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenThead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseThead = "<" Spnl "/" ("thead" | "THEAD") Spnl ">"
+ def _HtmlBlockCloseThead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("thead")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("THEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseThead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockThead = HtmlBlockOpenThead (HtmlBlockThead | !HtmlBlockCloseThead .)* HtmlBlockCloseThead
+ def _HtmlBlockThead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenThead)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockThead)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseThead)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseThead)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockThead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenTr = "<" Spnl ("tr" | "TR") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenTr
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tr")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TR")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenTr unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseTr = "<" Spnl "/" ("tr" | "TR") Spnl ">"
+ def _HtmlBlockCloseTr
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("tr")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("TR")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseTr unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockTr = HtmlBlockOpenTr (HtmlBlockTr | !HtmlBlockCloseTr .)* HtmlBlockCloseTr
+ def _HtmlBlockTr
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenTr)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockTr)
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_HtmlBlockCloseTr)
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseTr)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockTr unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenScript = "<" Spnl ("script" | "SCRIPT") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenScript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("script")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("SCRIPT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenScript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseScript = "<" Spnl "/" ("script" | "SCRIPT") Spnl ">"
+ def _HtmlBlockCloseScript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("script")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("SCRIPT")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseScript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockScript = HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript
+ def _HtmlBlockScript
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenScript)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = apply(:_HtmlBlockCloseScript)
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseScript)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockScript unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockOpenHead = "<" Spnl ("head" | "HEAD") Spnl HtmlAttribute* ">"
+ def _HtmlBlockOpenHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("head")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("HEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockOpenHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockCloseHead = "<" Spnl "/" ("head" | "HEAD") Spnl ">"
+ def _HtmlBlockCloseHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("head")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("HEAD")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockCloseHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockHead = HtmlBlockOpenHead (!HtmlBlockCloseHead .)* HtmlBlockCloseHead
+ def _HtmlBlockHead
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_HtmlBlockOpenHead)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = apply(:_HtmlBlockCloseHead)
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockCloseHead)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockHead unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockInTags = (HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript | HtmlBlockHead)
+ def _HtmlBlockInTags
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlAnchor)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockAddress)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockBlockquote)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockCenter)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockDir)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockDiv)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockDl)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockFieldset)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockForm)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH1)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH2)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH3)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH4)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH5)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockH6)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockMenu)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockNoframes)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockNoscript)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockOl)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockP)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockPre)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTable)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockUl)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockDd)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockDt)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockFrameset)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockLi)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTbody)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTd)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTfoot)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTh)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockThead)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockTr)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockScript)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_HtmlBlockHead)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_HtmlBlockInTags unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlock = < (HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing | HtmlUnclosed) > @BlankLine+ { if html? then RDoc::Markup::Raw.new text end }
+ def _HtmlBlock
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlockInTags)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_HtmlComment)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_HtmlBlockSelfClosing)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_HtmlUnclosed)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; if html? then
+ RDoc::Markup::Raw.new text
+ end ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlock unless _tmp
+ return _tmp
+ end
+
+ # HtmlUnclosed = "<" Spnl HtmlUnclosedType Spnl HtmlAttribute* Spnl ">"
+ def _HtmlUnclosed
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlUnclosedType)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlUnclosed unless _tmp
+ return _tmp
+ end
+
+ # HtmlUnclosedType = ("HR" | "hr")
+ def _HtmlUnclosedType
+
+ _save = self.pos
+ while true # choice
+ _tmp = match_string("HR")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("hr")
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_HtmlUnclosedType unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockSelfClosing = "<" Spnl HtmlBlockType Spnl HtmlAttribute* "/" Spnl ">"
+ def _HtmlBlockSelfClosing
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_HtmlBlockType)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlBlockSelfClosing unless _tmp
+ return _tmp
+ end
+
+ # HtmlBlockType = ("ADDRESS" | "BLOCKQUOTE" | "CENTER" | "DD" | "DIR" | "DIV" | "DL" | "DT" | "FIELDSET" | "FORM" | "FRAMESET" | "H1" | "H2" | "H3" | "H4" | "H5" | "H6" | "HR" | "ISINDEX" | "LI" | "MENU" | "NOFRAMES" | "NOSCRIPT" | "OL" | "P" | "PRE" | "SCRIPT" | "TABLE" | "TBODY" | "TD" | "TFOOT" | "TH" | "THEAD" | "TR" | "UL" | "address" | "blockquote" | "center" | "dd" | "dir" | "div" | "dl" | "dt" | "fieldset" | "form" | "frameset" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "hr" | "isindex" | "li" | "menu" | "noframes" | "noscript" | "ol" | "p" | "pre" | "script" | "table" | "tbody" | "td" | "tfoot" | "th" | "thead" | "tr" | "ul")
+ def _HtmlBlockType
+
+ _save = self.pos
+ while true # choice
+ _tmp = match_string("ADDRESS")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("BLOCKQUOTE")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("CENTER")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("DD")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("DIR")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("DIV")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("DL")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("DT")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("FIELDSET")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("FORM")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("FRAMESET")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H1")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H2")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H3")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H4")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H5")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("H6")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("HR")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("ISINDEX")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("LI")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("MENU")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("NOFRAMES")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("NOSCRIPT")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("OL")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("P")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("PRE")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("SCRIPT")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TABLE")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TBODY")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TD")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TFOOT")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TH")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("THEAD")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("TR")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("UL")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("address")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("blockquote")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("center")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("dd")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("dir")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("div")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("dl")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("dt")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("fieldset")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("form")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("frameset")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h1")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h2")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h3")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h4")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h5")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("h6")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("hr")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("isindex")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("li")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("menu")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("noframes")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("noscript")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("ol")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("p")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("pre")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("script")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("table")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("tbody")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("td")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("tfoot")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("th")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("thead")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("tr")
+ break if _tmp
+ self.pos = _save
+ _tmp = match_string("ul")
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_HtmlBlockType unless _tmp
+ return _tmp
+ end
+
+ # StyleOpen = "<" Spnl ("style" | "STYLE") Spnl HtmlAttribute* ">"
+ def _StyleOpen
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("style")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("STYLE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StyleOpen unless _tmp
+ return _tmp
+ end
+
+ # StyleClose = "<" Spnl "/" ("style" | "STYLE") Spnl ">"
+ def _StyleClose
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("/")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = match_string("style")
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("STYLE")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StyleClose unless _tmp
+ return _tmp
+ end
+
+ # InStyleTags = StyleOpen (!StyleClose .)* StyleClose
+ def _InStyleTags
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_StyleOpen)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = apply(:_StyleClose)
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_StyleClose)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_InStyleTags unless _tmp
+ return _tmp
+ end
+
+ # StyleBlock = < InStyleTags > @BlankLine* { if css? then RDoc::Markup::Raw.new text end }
+ def _StyleBlock
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = apply(:_InStyleTags)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; if css? then
+ RDoc::Markup::Raw.new text
+ end ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StyleBlock unless _tmp
+ return _tmp
+ end
+
+ # Inlines = (!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }
+ def _Inlines
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+
+ _save2 = self.pos
+ while true # choice
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Inline)
+ i = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; i ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = _Endline()
+ c = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _save6 = self.pos
+ _tmp = apply(:_Inline)
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; c ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ if _tmp
+ _ary << @result
+ while true
+
+ _save7 = self.pos
+ while true # choice
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = _Endline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Inline)
+ i = @result
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ @result = begin; i ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = _Endline()
+ c = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _save11 = self.pos
+ _tmp = apply(:_Inline)
+ self.pos = _save11
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; c ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+ break
+ end # end choice
+
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ chunks = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save12 = self.pos
+ _tmp = _Endline()
+ unless _tmp
+ _tmp = true
+ self.pos = _save12
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; chunks ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Inlines unless _tmp
+ return _tmp
+ end
+
+ # Inline = (Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)
+ def _Inline
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_Str)
+ break if _tmp
+ self.pos = _save
+ _tmp = _Endline()
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_UlOrStarLine)
+ break if _tmp
+ self.pos = _save
+ _tmp = _Space()
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Strong)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Emph)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Strike)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Image)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Link)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_NoteReference)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_InlineNote)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Code)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_RawHtml)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Entity)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_EscapedChar)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_Symbol)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Inline unless _tmp
+ return _tmp
+ end
+
+ # Space = @Spacechar+ { " " }
+ def _Space
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _Spacechar()
+ if _tmp
+ while true
+ _tmp = _Spacechar()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; " " ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Space unless _tmp
+ return _tmp
+ end
+
+ # Str = @StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }
+ def _Str
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _save1 = self.pos
+ _tmp = _NormalChar()
+ if _tmp
+ while true
+ _tmp = _NormalChar()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a = text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrChunk)
+ c = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << c ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Str unless _tmp
+ return _tmp
+ end
+
+ # StrChunk = < (@NormalChar | /_+/ &Alphanumeric)+ > { text }
+ def _StrChunk
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = _NormalChar()
+ break if _tmp
+ self.pos = _save2
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?-mix:_+)/)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save4 = self.pos
+ _tmp = apply(:_Alphanumeric)
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # choice
+ _tmp = _NormalChar()
+ break if _tmp
+ self.pos = _save5
+
+ _save6 = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?-mix:_+)/)
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _save7 = self.pos
+ _tmp = apply(:_Alphanumeric)
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save5
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StrChunk unless _tmp
+ return _tmp
+ end
+
+ # EscapedChar = "\\" !@Newline < /[:\\`|*_{}\[\]()#+.!><-]/ > { text }
+ def _EscapedChar
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("\\")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:[:\\`|*_{}\[\]()#+.!><-])/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_EscapedChar unless _tmp
+ return _tmp
+ end
+
+ # Entity = (HexEntity | DecEntity | CharEntity):a { a }
+ def _Entity
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_HexEntity)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_DecEntity)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_CharEntity)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Entity unless _tmp
+ return _tmp
+ end
+
+ # Endline = (@LineBreak | @TerminalEndline | @NormalEndline)
+ def _Endline
+
+ _save = self.pos
+ while true # choice
+ _tmp = _LineBreak()
+ break if _tmp
+ self.pos = _save
+ _tmp = _TerminalEndline()
+ break if _tmp
+ self.pos = _save
+ _tmp = _NormalEndline()
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Endline unless _tmp
+ return _tmp
+ end
+
+ # NormalEndline = @Sp @Newline !@BlankLine !">" !AtxStart !(Line /={1,}|-{1,}/ @Newline) { "\n" }
+ def _NormalEndline
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save2
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save3 = self.pos
+ _tmp = apply(:_AtxStart)
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save4 = self.pos
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = apply(:_Line)
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = scan(/\A(?-mix:={1,}|-{1,})/)
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "\n" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_NormalEndline unless _tmp
+ return _tmp
+ end
+
+ # TerminalEndline = @Sp @Newline @Eof
+ def _TerminalEndline
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Eof()
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TerminalEndline unless _tmp
+ return _tmp
+ end
+
+ # LineBreak = " " @NormalEndline { RDoc::Markup::HardBreak.new }
+ def _LineBreak
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string(" ")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _NormalEndline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::HardBreak.new ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_LineBreak unless _tmp
+ return _tmp
+ end
+
+ # Symbol = < @SpecialChar > { text }
+ def _Symbol
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = _SpecialChar()
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Symbol unless _tmp
+ return _tmp
+ end
+
+ # UlOrStarLine = (UlLine | StarLine):a { a }
+ def _UlOrStarLine
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_UlLine)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_StarLine)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_UlOrStarLine unless _tmp
+ return _tmp
+ end
+
+ # StarLine = (< /\*{4,}/ > { text } | < @Spacechar /\*+/ &@Spacechar > { text })
+ def _StarLine
+
+ _save = self.pos
+ while true # choice
+
+ _save1 = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:\*{4,})/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save1
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+
+ _save2 = self.pos
+ while true # sequence
+ _text_start = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = _Spacechar()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = scan(/\A(?-mix:\*+)/)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save4 = self.pos
+ _tmp = _Spacechar()
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_StarLine unless _tmp
+ return _tmp
+ end
+
+ # UlLine = (< /_{4,}/ > { text } | < @Spacechar /_+/ &@Spacechar > { text })
+ def _UlLine
+
+ _save = self.pos
+ while true # choice
+
+ _save1 = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:_{4,})/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save1
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+
+ _save2 = self.pos
+ while true # sequence
+ _text_start = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = _Spacechar()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = scan(/\A(?-mix:_+)/)
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save4 = self.pos
+ _tmp = _Spacechar()
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_UlLine unless _tmp
+ return _tmp
+ end
+
+ # Emph = (EmphStar | EmphUl)
+ def _Emph
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_EmphStar)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_EmphUl)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Emph unless _tmp
+ return _tmp
+ end
+
+ # Whitespace = (@Spacechar | @Newline)
+ def _Whitespace
+
+ _save = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Whitespace unless _tmp
+ return _tmp
+ end
+
+ # EmphStar = "*" !@Whitespace @StartList:a (!"*" Inline:b { a << b } | StrongStar:b { a << b })+ "*" { emphasis a.join }
+ def _EmphStar
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # choice
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = match_string("*")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
+
+ _save6 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongStar)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # choice
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("*")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongStar)
+ b = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("*")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; emphasis a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_EmphStar unless _tmp
+ return _tmp
+ end
+
+ # EmphUl = "_" !@Whitespace @StartList:a (!"_" Inline:b { a << b } | StrongUl:b { a << b })+ "_" { emphasis a.join }
+ def _EmphUl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # choice
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = match_string("_")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
+
+ _save6 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongUl)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save3
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # choice
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("_")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+
+ _save10 = self.pos
+ while true # sequence
+ _tmp = apply(:_StrongUl)
+ b = @result
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save7
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("_")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; emphasis a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_EmphUl unless _tmp
+ return _tmp
+ end
+
+ # Strong = (StrongStar | StrongUl)
+ def _Strong
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_StrongStar)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_StrongUl)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Strong unless _tmp
+ return _tmp
+ end
+
+ # StrongStar = "**" !@Whitespace @StartList:a (!"**" Inline:b { a << b })+ "**" { strong a.join }
+ def _StrongStar
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("**")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("**")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = match_string("**")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("**")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; strong a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StrongStar unless _tmp
+ return _tmp
+ end
+
+ # StrongUl = "__" !@Whitespace @StartList:a (!"__" Inline:b { a << b })+ "__" { strong a.join }
+ def _StrongUl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("__")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("__")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = match_string("__")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("__")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; strong a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StrongUl unless _tmp
+ return _tmp
+ end
+
+ # Strike = &{ strike? } "~~" !@Whitespace @StartList:a (!"~~" Inline:b { a << b })+ "~~" { strike a.join }
+ def _Strike
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; strike? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("~~")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = _Whitespace()
+ _tmp = _tmp ? nil : true
+ self.pos = _save2
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = match_string("~~")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = match_string("~~")
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = apply(:_Inline)
+ b = @result
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ @result = begin; a << b ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("~~")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; strike a.join ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Strike unless _tmp
+ return _tmp
+ end
+
+ # Image = "!" (ExplicitLink | ReferenceLink):a { "rdoc-image:#{a[/\[(.*)\]/, 1]}" }
+ def _Image
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("!")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_ExplicitLink)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_ReferenceLink)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "rdoc-image:#{a[/\[(.*)\]/, 1]}" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Image unless _tmp
+ return _tmp
+ end
+
+ # Link = (ExplicitLink | ReferenceLink | AutoLink)
+ def _Link
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_ExplicitLink)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_ReferenceLink)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_AutoLink)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Link unless _tmp
+ return _tmp
+ end
+
+ # ReferenceLink = (ReferenceLinkDouble | ReferenceLinkSingle)
+ def _ReferenceLink
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_ReferenceLinkDouble)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_ReferenceLinkSingle)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_ReferenceLink unless _tmp
+ return _tmp
+ end
+
+ # ReferenceLinkDouble = Label:content < Spnl > !"[]" Label:label { link_to content, label, text }
+ def _ReferenceLinkDouble
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Label)
+ content = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _tmp = apply(:_Spnl)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("[]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Label)
+ label = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; link_to content, label, text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ReferenceLinkDouble unless _tmp
+ return _tmp
+ end
+
+ # ReferenceLinkSingle = Label:content < (Spnl "[]")? > { link_to content, content, text }
+ def _ReferenceLinkSingle
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Label)
+ content = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = match_string("[]")
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; link_to content, content, text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ReferenceLinkSingle unless _tmp
+ return _tmp
+ end
+
+ # ExplicitLink = Label:l "(" @Sp Source:s Spnl Title @Sp ")" { "{#{l}}[#{s}]" }
+ def _ExplicitLink
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Label)
+ l = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("(")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Source)
+ s = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Title)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(")")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "{#{l}}[#{s}]" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ExplicitLink unless _tmp
+ return _tmp
+ end
+
+ # Source = ("<" < SourceContents > ">" | < SourceContents >) { text }
+ def _Source
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _text_start = self.pos
+ _tmp = apply(:_SourceContents)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ _text_start = self.pos
+ _tmp = apply(:_SourceContents)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Source unless _tmp
+ return _tmp
+ end
+
+ # SourceContents = ((!"(" !")" !">" Nonspacechar)+ | "(" SourceContents ")")*
+ def _SourceContents
+ while true
+
+ _save1 = self.pos
+ while true # choice
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("(")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string(")")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save6 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # sequence
+ _save8 = self.pos
+ _tmp = match_string("(")
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _save9 = self.pos
+ _tmp = match_string(")")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _save10 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save10
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ break if _tmp
+ self.pos = _save1
+
+ _save11 = self.pos
+ while true # sequence
+ _tmp = match_string("(")
+ unless _tmp
+ self.pos = _save11
+ break
+ end
+ _tmp = apply(:_SourceContents)
+ unless _tmp
+ self.pos = _save11
+ break
+ end
+ _tmp = match_string(")")
+ unless _tmp
+ self.pos = _save11
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ set_failed_rule :_SourceContents unless _tmp
+ return _tmp
+ end
+
+ # Title = (TitleSingle | TitleDouble | ""):a { a }
+ def _Title
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_TitleSingle)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_TitleDouble)
+ break if _tmp
+ self.pos = _save1
+ _tmp = match_string("")
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Title unless _tmp
+ return _tmp
+ end
+
+ # TitleSingle = "'" (!("'" @Sp (")" | @Newline)) .)* "'"
+ def _TitleSingle
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+
+ _save5 = self.pos
+ while true # choice
+ _tmp = match_string(")")
+ break if _tmp
+ self.pos = _save5
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save5
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TitleSingle unless _tmp
+ return _tmp
+ end
+
+ # TitleDouble = "\"" (!("\"" @Sp (")" | @Newline)) .)* "\""
+ def _TitleDouble
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+
+ _save5 = self.pos
+ while true # choice
+ _tmp = match_string(")")
+ break if _tmp
+ self.pos = _save5
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save5
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TitleDouble unless _tmp
+ return _tmp
+ end
+
+ # AutoLink = (AutoLinkUrl | AutoLinkEmail)
+ def _AutoLink
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_AutoLinkUrl)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_AutoLinkEmail)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_AutoLink unless _tmp
+ return _tmp
+ end
+
+ # AutoLinkUrl = "<" < /[A-Za-z]+/ "://" (!@Newline !">" .)+ > ">" { text }
+ def _AutoLinkUrl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+
+ _save1 = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?-mix:[A-Za-z]+)/)
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ _tmp = match_string("://")
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save5 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _save8 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save1
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_AutoLinkUrl unless _tmp
+ return _tmp
+ end
+
+ # AutoLinkEmail = "<" "mailto:"? < /[\w+.\/!%~$-]+/i "@" (!@Newline !">" .)+ > ">" { "mailto:#{text}" }
+ def _AutoLinkEmail
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("mailto:")
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?i-mx:[\w+.\/!%~$-]+)/)
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = match_string("@")
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _save6 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save7 = self.pos
+ while true # sequence
+ _save8 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _save9 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "mailto:#{text}" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_AutoLinkEmail unless _tmp
+ return _tmp
+ end
+
+ # Reference = @NonindentSpace !"[]" Label:label ":" Spnl RefSrc:link RefTitle @BlankLine+ { # TODO use title reference label, link nil }
+ def _Reference
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("[]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Label)
+ label = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(":")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_RefSrc)
+ link = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_RefTitle)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; # TODO use title
+ reference label, link
+ nil
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Reference unless _tmp
+ return _tmp
+ end
+
+ # Label = "[" (!"^" &{ notes? } | &. &{ !notes? }) @StartList:a (!"]" Inline:l { a << l })* "]" { a.join.gsub(/\s+/, ' ') }
+ def _Label
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("[")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+
+ _save1 = self.pos
+ while true # choice
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = match_string("^")
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save4 = self.pos
+ _tmp = begin; notes? ; end
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = get_byte
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _save7 = self.pos
+ _tmp = begin; !notes? ; end
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save9 = self.pos
+ while true # sequence
+ _save10 = self.pos
+ _tmp = match_string("]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save10
+ unless _tmp
+ self.pos = _save9
+ break
+ end
+ _tmp = apply(:_Inline)
+ l = @result
+ unless _tmp
+ self.pos = _save9
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save9
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("]")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a.join.gsub(/\s+/, ' ') ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Label unless _tmp
+ return _tmp
+ end
+
+ # RefSrc = < Nonspacechar+ > { text }
+ def _RefSrc
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _save1 = self.pos
+ _tmp = apply(:_Nonspacechar)
+ if _tmp
+ while true
+ _tmp = apply(:_Nonspacechar)
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RefSrc unless _tmp
+ return _tmp
+ end
+
+ # RefTitle = (RefTitleSingle | RefTitleDouble | RefTitleParens | EmptyTitle)
+ def _RefTitle
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_RefTitleSingle)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_RefTitleDouble)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_RefTitleParens)
+ break if _tmp
+ self.pos = _save
+ _tmp = apply(:_EmptyTitle)
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_RefTitle unless _tmp
+ return _tmp
+ end
+
+ # EmptyTitle = ""
+ def _EmptyTitle
+ _tmp = match_string("")
+ set_failed_rule :_EmptyTitle unless _tmp
+ return _tmp
+ end
+
+ # RefTitleSingle = Spnl "'" < (!("'" @Sp @Newline | @Newline) .)* > "'" { text }
+ def _RefTitleSingle
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # choice
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save4
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save4
+ break
+ end # end choice
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RefTitleSingle unless _tmp
+ return _tmp
+ end
+
+ # RefTitleDouble = Spnl "\"" < (!("\"" @Sp @Newline | @Newline) .)* > "\"" { text }
+ def _RefTitleDouble
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # choice
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save4
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save4
+ break
+ end # end choice
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RefTitleDouble unless _tmp
+ return _tmp
+ end
+
+ # RefTitleParens = Spnl "(" < (!(")" @Sp @Newline | @Newline) .)* > ")" { text }
+ def _RefTitleParens
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("(")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # choice
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string(")")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save4
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save4
+ break
+ end # end choice
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(")")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RefTitleParens unless _tmp
+ return _tmp
+ end
+
+ # References = (Reference | SkipBlock)*
+ def _References
+ while true
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_Reference)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_SkipBlock)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ set_failed_rule :_References unless _tmp
+ return _tmp
+ end
+
+ # Ticks1 = "`" !"`"
+ def _Ticks1
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("`")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Ticks1 unless _tmp
+ return _tmp
+ end
+
+ # Ticks2 = "``" !"`"
+ def _Ticks2
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("``")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Ticks2 unless _tmp
+ return _tmp
+ end
+
+ # Ticks3 = "```" !"`"
+ def _Ticks3
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("```")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Ticks3 unless _tmp
+ return _tmp
+ end
+
+ # Ticks4 = "````" !"`"
+ def _Ticks4
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("````")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Ticks4 unless _tmp
+ return _tmp
+ end
+
+ # Ticks5 = "`````" !"`"
+ def _Ticks5
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("`````")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Ticks5 unless _tmp
+ return _tmp
+ end
+
+ # Code = (Ticks1 @Sp < ((!"`" Nonspacechar)+ | !Ticks1 /`+/ | !(@Sp Ticks1) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks1 | Ticks2 @Sp < ((!"`" Nonspacechar)+ | !Ticks2 /`+/ | !(@Sp Ticks2) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks2 | Ticks3 @Sp < ((!"`" Nonspacechar)+ | !Ticks3 /`+/ | !(@Sp Ticks3) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks3 | Ticks4 @Sp < ((!"`" Nonspacechar)+ | !Ticks4 /`+/ | !(@Sp Ticks4) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks4 | Ticks5 @Sp < ((!"`" Nonspacechar)+ | !Ticks5 /`+/ | !(@Sp Ticks5) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks5) { "<code>#{text}</code>" }
+ def _Code
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = apply(:_Ticks1)
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _text_start = self.pos
+ _save3 = self.pos
+
+ _save4 = self.pos
+ while true # choice
+ _save5 = self.pos
+
+ _save6 = self.pos
+ while true # sequence
+ _save7 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save6
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save6
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save5
+ end
+ break if _tmp
+ self.pos = _save4
+
+ _save10 = self.pos
+ while true # sequence
+ _save11 = self.pos
+ _tmp = apply(:_Ticks1)
+ _tmp = _tmp ? nil : true
+ self.pos = _save11
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save4
+
+ _save12 = self.pos
+ while true # sequence
+ _save13 = self.pos
+
+ _save14 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save14
+ break
+ end
+ _tmp = apply(:_Ticks1)
+ unless _tmp
+ self.pos = _save14
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save13
+ unless _tmp
+ self.pos = _save12
+ break
+ end
+
+ _save15 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save15
+
+ _save16 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save16
+ break
+ end
+ _save17 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save17
+ unless _tmp
+ self.pos = _save16
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save15
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save12
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save4
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save18 = self.pos
+ while true # choice
+ _save19 = self.pos
+
+ _save20 = self.pos
+ while true # sequence
+ _save21 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save21
+ unless _tmp
+ self.pos = _save20
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save20
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save22 = self.pos
+ while true # sequence
+ _save23 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save23
+ unless _tmp
+ self.pos = _save22
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save22
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save19
+ end
+ break if _tmp
+ self.pos = _save18
+
+ _save24 = self.pos
+ while true # sequence
+ _save25 = self.pos
+ _tmp = apply(:_Ticks1)
+ _tmp = _tmp ? nil : true
+ self.pos = _save25
+ unless _tmp
+ self.pos = _save24
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save24
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save18
+
+ _save26 = self.pos
+ while true # sequence
+ _save27 = self.pos
+
+ _save28 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save28
+ break
+ end
+ _tmp = apply(:_Ticks1)
+ unless _tmp
+ self.pos = _save28
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save27
+ unless _tmp
+ self.pos = _save26
+ break
+ end
+
+ _save29 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save29
+
+ _save30 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save30
+ break
+ end
+ _save31 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save31
+ unless _tmp
+ self.pos = _save30
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save29
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save26
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save18
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = apply(:_Ticks1)
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save32 = self.pos
+ while true # sequence
+ _tmp = apply(:_Ticks2)
+ unless _tmp
+ self.pos = _save32
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save32
+ break
+ end
+ _text_start = self.pos
+ _save33 = self.pos
+
+ _save34 = self.pos
+ while true # choice
+ _save35 = self.pos
+
+ _save36 = self.pos
+ while true # sequence
+ _save37 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save37
+ unless _tmp
+ self.pos = _save36
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save36
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save38 = self.pos
+ while true # sequence
+ _save39 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save39
+ unless _tmp
+ self.pos = _save38
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save38
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save35
+ end
+ break if _tmp
+ self.pos = _save34
+
+ _save40 = self.pos
+ while true # sequence
+ _save41 = self.pos
+ _tmp = apply(:_Ticks2)
+ _tmp = _tmp ? nil : true
+ self.pos = _save41
+ unless _tmp
+ self.pos = _save40
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save40
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save34
+
+ _save42 = self.pos
+ while true # sequence
+ _save43 = self.pos
+
+ _save44 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save44
+ break
+ end
+ _tmp = apply(:_Ticks2)
+ unless _tmp
+ self.pos = _save44
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save43
+ unless _tmp
+ self.pos = _save42
+ break
+ end
+
+ _save45 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save45
+
+ _save46 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save46
+ break
+ end
+ _save47 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save47
+ unless _tmp
+ self.pos = _save46
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save45
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save42
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save34
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save48 = self.pos
+ while true # choice
+ _save49 = self.pos
+
+ _save50 = self.pos
+ while true # sequence
+ _save51 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save51
+ unless _tmp
+ self.pos = _save50
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save50
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save52 = self.pos
+ while true # sequence
+ _save53 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save53
+ unless _tmp
+ self.pos = _save52
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save52
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save49
+ end
+ break if _tmp
+ self.pos = _save48
+
+ _save54 = self.pos
+ while true # sequence
+ _save55 = self.pos
+ _tmp = apply(:_Ticks2)
+ _tmp = _tmp ? nil : true
+ self.pos = _save55
+ unless _tmp
+ self.pos = _save54
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save54
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save48
+
+ _save56 = self.pos
+ while true # sequence
+ _save57 = self.pos
+
+ _save58 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save58
+ break
+ end
+ _tmp = apply(:_Ticks2)
+ unless _tmp
+ self.pos = _save58
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save57
+ unless _tmp
+ self.pos = _save56
+ break
+ end
+
+ _save59 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save59
+
+ _save60 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save60
+ break
+ end
+ _save61 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save61
+ unless _tmp
+ self.pos = _save60
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save59
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save56
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save48
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save33
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save32
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save32
+ break
+ end
+ _tmp = apply(:_Ticks2)
+ unless _tmp
+ self.pos = _save32
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save62 = self.pos
+ while true # sequence
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save62
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save62
+ break
+ end
+ _text_start = self.pos
+ _save63 = self.pos
+
+ _save64 = self.pos
+ while true # choice
+ _save65 = self.pos
+
+ _save66 = self.pos
+ while true # sequence
+ _save67 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save67
+ unless _tmp
+ self.pos = _save66
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save66
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save68 = self.pos
+ while true # sequence
+ _save69 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save69
+ unless _tmp
+ self.pos = _save68
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save68
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save65
+ end
+ break if _tmp
+ self.pos = _save64
+
+ _save70 = self.pos
+ while true # sequence
+ _save71 = self.pos
+ _tmp = apply(:_Ticks3)
+ _tmp = _tmp ? nil : true
+ self.pos = _save71
+ unless _tmp
+ self.pos = _save70
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save70
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save64
+
+ _save72 = self.pos
+ while true # sequence
+ _save73 = self.pos
+
+ _save74 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save74
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save74
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save73
+ unless _tmp
+ self.pos = _save72
+ break
+ end
+
+ _save75 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save75
+
+ _save76 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save76
+ break
+ end
+ _save77 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save77
+ unless _tmp
+ self.pos = _save76
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save75
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save72
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save64
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save78 = self.pos
+ while true # choice
+ _save79 = self.pos
+
+ _save80 = self.pos
+ while true # sequence
+ _save81 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save81
+ unless _tmp
+ self.pos = _save80
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save80
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save82 = self.pos
+ while true # sequence
+ _save83 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save83
+ unless _tmp
+ self.pos = _save82
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save82
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save79
+ end
+ break if _tmp
+ self.pos = _save78
+
+ _save84 = self.pos
+ while true # sequence
+ _save85 = self.pos
+ _tmp = apply(:_Ticks3)
+ _tmp = _tmp ? nil : true
+ self.pos = _save85
+ unless _tmp
+ self.pos = _save84
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save84
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save78
+
+ _save86 = self.pos
+ while true # sequence
+ _save87 = self.pos
+
+ _save88 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save88
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save88
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save87
+ unless _tmp
+ self.pos = _save86
+ break
+ end
+
+ _save89 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save89
+
+ _save90 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save90
+ break
+ end
+ _save91 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save91
+ unless _tmp
+ self.pos = _save90
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save89
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save86
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save78
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save63
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save62
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save62
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save62
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save92 = self.pos
+ while true # sequence
+ _tmp = apply(:_Ticks4)
+ unless _tmp
+ self.pos = _save92
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save92
+ break
+ end
+ _text_start = self.pos
+ _save93 = self.pos
+
+ _save94 = self.pos
+ while true # choice
+ _save95 = self.pos
+
+ _save96 = self.pos
+ while true # sequence
+ _save97 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save97
+ unless _tmp
+ self.pos = _save96
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save96
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save98 = self.pos
+ while true # sequence
+ _save99 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save99
+ unless _tmp
+ self.pos = _save98
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save98
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save95
+ end
+ break if _tmp
+ self.pos = _save94
+
+ _save100 = self.pos
+ while true # sequence
+ _save101 = self.pos
+ _tmp = apply(:_Ticks4)
+ _tmp = _tmp ? nil : true
+ self.pos = _save101
+ unless _tmp
+ self.pos = _save100
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save100
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save94
+
+ _save102 = self.pos
+ while true # sequence
+ _save103 = self.pos
+
+ _save104 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save104
+ break
+ end
+ _tmp = apply(:_Ticks4)
+ unless _tmp
+ self.pos = _save104
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save103
+ unless _tmp
+ self.pos = _save102
+ break
+ end
+
+ _save105 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save105
+
+ _save106 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save106
+ break
+ end
+ _save107 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save107
+ unless _tmp
+ self.pos = _save106
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save105
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save102
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save94
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save108 = self.pos
+ while true # choice
+ _save109 = self.pos
+
+ _save110 = self.pos
+ while true # sequence
+ _save111 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save111
+ unless _tmp
+ self.pos = _save110
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save110
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save112 = self.pos
+ while true # sequence
+ _save113 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save113
+ unless _tmp
+ self.pos = _save112
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save112
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save109
+ end
+ break if _tmp
+ self.pos = _save108
+
+ _save114 = self.pos
+ while true # sequence
+ _save115 = self.pos
+ _tmp = apply(:_Ticks4)
+ _tmp = _tmp ? nil : true
+ self.pos = _save115
+ unless _tmp
+ self.pos = _save114
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save114
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save108
+
+ _save116 = self.pos
+ while true # sequence
+ _save117 = self.pos
+
+ _save118 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save118
+ break
+ end
+ _tmp = apply(:_Ticks4)
+ unless _tmp
+ self.pos = _save118
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save117
+ unless _tmp
+ self.pos = _save116
+ break
+ end
+
+ _save119 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save119
+
+ _save120 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save120
+ break
+ end
+ _save121 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save121
+ unless _tmp
+ self.pos = _save120
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save119
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save116
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save108
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save93
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save92
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save92
+ break
+ end
+ _tmp = apply(:_Ticks4)
+ unless _tmp
+ self.pos = _save92
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save122 = self.pos
+ while true # sequence
+ _tmp = apply(:_Ticks5)
+ unless _tmp
+ self.pos = _save122
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save122
+ break
+ end
+ _text_start = self.pos
+ _save123 = self.pos
+
+ _save124 = self.pos
+ while true # choice
+ _save125 = self.pos
+
+ _save126 = self.pos
+ while true # sequence
+ _save127 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save127
+ unless _tmp
+ self.pos = _save126
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save126
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save128 = self.pos
+ while true # sequence
+ _save129 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save129
+ unless _tmp
+ self.pos = _save128
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save128
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save125
+ end
+ break if _tmp
+ self.pos = _save124
+
+ _save130 = self.pos
+ while true # sequence
+ _save131 = self.pos
+ _tmp = apply(:_Ticks5)
+ _tmp = _tmp ? nil : true
+ self.pos = _save131
+ unless _tmp
+ self.pos = _save130
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save130
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save124
+
+ _save132 = self.pos
+ while true # sequence
+ _save133 = self.pos
+
+ _save134 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save134
+ break
+ end
+ _tmp = apply(:_Ticks5)
+ unless _tmp
+ self.pos = _save134
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save133
+ unless _tmp
+ self.pos = _save132
+ break
+ end
+
+ _save135 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save135
+
+ _save136 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save136
+ break
+ end
+ _save137 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save137
+ unless _tmp
+ self.pos = _save136
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save135
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save132
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save124
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save138 = self.pos
+ while true # choice
+ _save139 = self.pos
+
+ _save140 = self.pos
+ while true # sequence
+ _save141 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save141
+ unless _tmp
+ self.pos = _save140
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save140
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save142 = self.pos
+ while true # sequence
+ _save143 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save143
+ unless _tmp
+ self.pos = _save142
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save142
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save139
+ end
+ break if _tmp
+ self.pos = _save138
+
+ _save144 = self.pos
+ while true # sequence
+ _save145 = self.pos
+ _tmp = apply(:_Ticks5)
+ _tmp = _tmp ? nil : true
+ self.pos = _save145
+ unless _tmp
+ self.pos = _save144
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save144
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save138
+
+ _save146 = self.pos
+ while true # sequence
+ _save147 = self.pos
+
+ _save148 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save148
+ break
+ end
+ _tmp = apply(:_Ticks5)
+ unless _tmp
+ self.pos = _save148
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save147
+ unless _tmp
+ self.pos = _save146
+ break
+ end
+
+ _save149 = self.pos
+ while true # choice
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save149
+
+ _save150 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save150
+ break
+ end
+ _save151 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save151
+ unless _tmp
+ self.pos = _save150
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save149
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save146
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save138
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save123
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save122
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save122
+ break
+ end
+ _tmp = apply(:_Ticks5)
+ unless _tmp
+ self.pos = _save122
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "<code>#{text}</code>" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Code unless _tmp
+ return _tmp
+ end
+
+ # RawHtml = < (HtmlComment | HtmlBlockScript | HtmlTag) > { if html? then text else '' end }
+ def _RawHtml
+
+ _save = self.pos
+ while true # sequence
+ _text_start = self.pos
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlComment)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_HtmlBlockScript)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_HtmlTag)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; if html? then text else '' end ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RawHtml unless _tmp
+ return _tmp
+ end
+
+ # BlankLine = @Sp @Newline { "\n" }
+ def _BlankLine
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; "\n" ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_BlankLine unless _tmp
+ return _tmp
+ end
+
+ # Quoted = ("\"" (!"\"" .)* "\"" | "'" (!"'" .)* "'")
+ def _Quoted
+
+ _save = self.pos
+ while true # choice
+
+ _save1 = self.pos
+ while true # sequence
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ while true
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("\"")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ _tmp = match_string("\"")
+ unless _tmp
+ self.pos = _save1
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ while true
+
+ _save7 = self.pos
+ while true # sequence
+ _save8 = self.pos
+ _tmp = match_string("'")
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = match_string("'")
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_Quoted unless _tmp
+ return _tmp
+ end
+
+ # HtmlAttribute = (AlphanumericAscii | "-")+ Spnl ("=" Spnl (Quoted | (!">" Nonspacechar)+))? Spnl
+ def _HtmlAttribute
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = apply(:_AlphanumericAscii)
+ break if _tmp
+ self.pos = _save2
+ _tmp = match_string("-")
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save3 = self.pos
+ while true # choice
+ _tmp = apply(:_AlphanumericAscii)
+ break if _tmp
+ self.pos = _save3
+ _tmp = match_string("-")
+ break if _tmp
+ self.pos = _save3
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save4 = self.pos
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string("=")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+
+ _save6 = self.pos
+ while true # choice
+ _tmp = apply(:_Quoted)
+ break if _tmp
+ self.pos = _save6
+ _save7 = self.pos
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save10 = self.pos
+ while true # sequence
+ _save11 = self.pos
+ _tmp = match_string(">")
+ _tmp = _tmp ? nil : true
+ self.pos = _save11
+ unless _tmp
+ self.pos = _save10
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save10
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save7
+ end
+ break if _tmp
+ self.pos = _save6
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ unless _tmp
+ _tmp = true
+ self.pos = _save4
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlAttribute unless _tmp
+ return _tmp
+ end
+
+ # HtmlComment = "<!--" (!"-->" .)* "-->"
+ def _HtmlComment
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<!--")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = match_string("-->")
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("-->")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlComment unless _tmp
+ return _tmp
+ end
+
+ # HtmlTag = "<" Spnl "/"? AlphanumericAscii+ Spnl HtmlAttribute* "/"? Spnl ">"
+ def _HtmlTag
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("<")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = match_string("/")
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = apply(:_AlphanumericAscii)
+ if _tmp
+ while true
+ _tmp = apply(:_AlphanumericAscii)
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = apply(:_HtmlAttribute)
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save4 = self.pos
+ _tmp = match_string("/")
+ unless _tmp
+ _tmp = true
+ self.pos = _save4
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(">")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HtmlTag unless _tmp
+ return _tmp
+ end
+
+ # Eof = !.
+ def _Eof
+ _save = self.pos
+ _tmp = get_byte
+ _tmp = _tmp ? nil : true
+ self.pos = _save
+ set_failed_rule :_Eof unless _tmp
+ return _tmp
+ end
+
+ # Nonspacechar = !@Spacechar !@Newline .
+ def _Nonspacechar
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = _Spacechar()
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save2
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Nonspacechar unless _tmp
+ return _tmp
+ end
+
+ # Sp = @Spacechar*
+ def _Sp
+ while true
+ _tmp = _Spacechar()
+ break unless _tmp
+ end
+ _tmp = true
+ set_failed_rule :_Sp unless _tmp
+ return _tmp
+ end
+
+ # Spnl = @Sp (@Newline @Sp)?
+ def _Spnl
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Spnl unless _tmp
+ return _tmp
+ end
+
+ # SpecialChar = (/[~*_`&\[\]()<!#\\'"]/ | @ExtendedSpecialChar)
+ def _SpecialChar
+
+ _save = self.pos
+ while true # choice
+ _tmp = scan(/\A(?-mix:[~*_`&\[\]()<!#\\'"])/)
+ break if _tmp
+ self.pos = _save
+ _tmp = _ExtendedSpecialChar()
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_SpecialChar unless _tmp
+ return _tmp
+ end
+
+ # NormalChar = !(@SpecialChar | @Spacechar | @Newline) .
+ def _NormalChar
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # choice
+ _tmp = _SpecialChar()
+ break if _tmp
+ self.pos = _save2
+ _tmp = _Spacechar()
+ break if _tmp
+ self.pos = _save2
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save2
+ break
+ end # end choice
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_NormalChar unless _tmp
+ return _tmp
+ end
+
+ # Digit = [0-9]
+ def _Digit
+ _save = self.pos
+ _tmp = get_byte
+ if _tmp
+ unless _tmp >= 48 and _tmp <= 57
+ self.pos = _save
+ _tmp = nil
+ end
+ end
+ set_failed_rule :_Digit unless _tmp
+ return _tmp
+ end
+
+ # Alphanumeric = %literals.Alphanumeric
+ def _Alphanumeric
+ _tmp = @_grammar_literals.external_invoke(self, :_Alphanumeric)
+ set_failed_rule :_Alphanumeric unless _tmp
+ return _tmp
+ end
+
+ # AlphanumericAscii = %literals.AlphanumericAscii
+ def _AlphanumericAscii
+ _tmp = @_grammar_literals.external_invoke(self, :_AlphanumericAscii)
+ set_failed_rule :_AlphanumericAscii unless _tmp
+ return _tmp
+ end
+
+ # BOM = %literals.BOM
+ def _BOM
+ _tmp = @_grammar_literals.external_invoke(self, :_BOM)
+ set_failed_rule :_BOM unless _tmp
+ return _tmp
+ end
+
+ # Newline = %literals.Newline
+ def _Newline
+ _tmp = @_grammar_literals.external_invoke(self, :_Newline)
+ set_failed_rule :_Newline unless _tmp
+ return _tmp
+ end
+
+ # Spacechar = %literals.Spacechar
+ def _Spacechar
+ _tmp = @_grammar_literals.external_invoke(self, :_Spacechar)
+ set_failed_rule :_Spacechar unless _tmp
+ return _tmp
+ end
+
+ # HexEntity = /&#x/i < /[0-9a-fA-F]+/ > ";" { [text.to_i(16)].pack 'U' }
+ def _HexEntity
+
+ _save = self.pos
+ while true # sequence
+ _tmp = scan(/\A(?i-mx:&#x)/)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:[0-9a-fA-F]+)/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(";")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; [text.to_i(16)].pack 'U' ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_HexEntity unless _tmp
+ return _tmp
+ end
+
+ # DecEntity = "&#" < /[0-9]+/ > ";" { [text.to_i].pack 'U' }
+ def _DecEntity
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("&#")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:[0-9]+)/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(";")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; [text.to_i].pack 'U' ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_DecEntity unless _tmp
+ return _tmp
+ end
+
+ # CharEntity = "&" < /[A-Za-z0-9]+/ > ";" { if entity = HTML_ENTITIES[text] then entity.pack 'U*' else "&#{text};" end }
+ def _CharEntity
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("&")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _tmp = scan(/\A(?-mix:[A-Za-z0-9]+)/)
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(";")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; if entity = HTML_ENTITIES[text] then
+ entity.pack 'U*'
+ else
+ "&#{text};"
+ end
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_CharEntity unless _tmp
+ return _tmp
+ end
+
+ # NonindentSpace = / {0,3}/
+ def _NonindentSpace
+ _tmp = scan(/\A(?-mix: {0,3})/)
+ set_failed_rule :_NonindentSpace unless _tmp
+ return _tmp
+ end
+
+ # Indent = /\t| /
+ def _Indent
+ _tmp = scan(/\A(?-mix:\t| )/)
+ set_failed_rule :_Indent unless _tmp
+ return _tmp
+ end
+
+ # IndentedLine = Indent Line
+ def _IndentedLine
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_Indent)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Line)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_IndentedLine unless _tmp
+ return _tmp
+ end
+
+ # OptionallyIndentedLine = Indent? Line
+ def _OptionallyIndentedLine
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = apply(:_Indent)
+ unless _tmp
+ _tmp = true
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Line)
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_OptionallyIndentedLine unless _tmp
+ return _tmp
+ end
+
+ # StartList = &. { [] }
+ def _StartList
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = get_byte
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; [] ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_StartList unless _tmp
+ return _tmp
+ end
+
+ # Line = @RawLine:a { a }
+ def _Line
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _RawLine()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Line unless _tmp
+ return _tmp
+ end
+
+ # RawLine = (< (!"\r" !"\n" .)* @Newline > | < .+ > @Eof) { text }
+ def _RawLine
+
+ _save = self.pos
+ while true # sequence
+
+ _save1 = self.pos
+ while true # choice
+ _text_start = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ while true
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = match_string("\r")
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _save6 = self.pos
+ _tmp = match_string("\n")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ break if _tmp
+ self.pos = _save1
+
+ _save7 = self.pos
+ while true # sequence
+ _text_start = self.pos
+ _save8 = self.pos
+ _tmp = get_byte
+ if _tmp
+ while true
+ _tmp = get_byte
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save8
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = _Eof()
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RawLine unless _tmp
+ return _tmp
+ end
+
+ # SkipBlock = (HtmlBlock | (!"#" !SetextBottom1 !SetextBottom2 !@BlankLine @RawLine)+ @BlankLine* | @BlankLine+ | @RawLine)
+ def _SkipBlock
+
+ _save = self.pos
+ while true # choice
+ _tmp = apply(:_HtmlBlock)
+ break if _tmp
+ self.pos = _save
+
+ _save1 = self.pos
+ while true # sequence
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("#")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save5 = self.pos
+ _tmp = apply(:_SetextBottom1)
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save6 = self.pos
+ _tmp = apply(:_SetextBottom2)
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _save7 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = _RawLine()
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save8 = self.pos
+ while true # sequence
+ _save9 = self.pos
+ _tmp = match_string("#")
+ _tmp = _tmp ? nil : true
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _save10 = self.pos
+ _tmp = apply(:_SetextBottom1)
+ _tmp = _tmp ? nil : true
+ self.pos = _save10
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _save11 = self.pos
+ _tmp = apply(:_SetextBottom2)
+ _tmp = _tmp ? nil : true
+ self.pos = _save11
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _save12 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save12
+ unless _tmp
+ self.pos = _save8
+ break
+ end
+ _tmp = _RawLine()
+ unless _tmp
+ self.pos = _save8
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save1
+ break
+ end
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save1
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save
+ _save14 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save14
+ end
+ break if _tmp
+ self.pos = _save
+ _tmp = _RawLine()
+ break if _tmp
+ self.pos = _save
+ break
+ end # end choice
+
+ set_failed_rule :_SkipBlock unless _tmp
+ return _tmp
+ end
+
+ # ExtendedSpecialChar = &{ notes? } "^"
+ def _ExtendedSpecialChar
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; notes? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("^")
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_ExtendedSpecialChar unless _tmp
+ return _tmp
+ end
+
+ # NoteReference = &{ notes? } RawNoteReference:ref { note_for ref }
+ def _NoteReference
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; notes? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_RawNoteReference)
+ ref = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; note_for ref ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_NoteReference unless _tmp
+ return _tmp
+ end
+
+ # RawNoteReference = "[^" < (!@Newline !"]" .)+ > "]" { text }
+ def _RawNoteReference
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("[^")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save4 = self.pos
+ _tmp = match_string("]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _save7 = self.pos
+ _tmp = match_string("]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("]")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RawNoteReference unless _tmp
+ return _tmp
+ end
+
+ # Note = &{ notes? } @NonindentSpace RawNoteReference:ref ":" @Sp @StartList:a RawNoteBlock:i { a.concat i } (&Indent RawNoteBlock:i { a.concat i })* { @footnotes[ref] = paragraph a nil }
+ def _Note
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; notes? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_RawNoteReference)
+ ref = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(":")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_RawNoteBlock)
+ i = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a.concat i ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = apply(:_Indent)
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_RawNoteBlock)
+ i = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a.concat i ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; @footnotes[ref] = paragraph a
+
+ nil
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Note unless _tmp
+ return _tmp
+ end
+
+ # InlineNote = &{ notes? } "^[" @StartList:a (!"]" Inline:l { a << l })+ "]" { ref = [:inline, @note_order.length] @footnotes[ref] = paragraph a note_for ref }
+ def _InlineNote
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; notes? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("^[")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _save4 = self.pos
+ _tmp = match_string("]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_Inline)
+ l = @result
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = match_string("]")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = apply(:_Inline)
+ l = @result
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("]")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; ref = [:inline, @note_order.length]
+ @footnotes[ref] = paragraph a
+
+ note_for ref
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_InlineNote unless _tmp
+ return _tmp
+ end
+
+ # Notes = (Note | SkipBlock)*
+ def _Notes
+ while true
+
+ _save1 = self.pos
+ while true # choice
+ _tmp = apply(:_Note)
+ break if _tmp
+ self.pos = _save1
+ _tmp = apply(:_SkipBlock)
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ set_failed_rule :_Notes unless _tmp
+ return _tmp
+ end
+
+ # RawNoteBlock = @StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }
+ def _RawNoteBlock
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _StartList()
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = apply(:_OptionallyIndentedLine)
+ l = @result
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save4 = self.pos
+ while true # sequence
+ _save5 = self.pos
+ _tmp = _BlankLine()
+ _tmp = _tmp ? nil : true
+ self.pos = _save5
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ _tmp = apply(:_OptionallyIndentedLine)
+ l = @result
+ unless _tmp
+ self.pos = _save4
+ break
+ end
+ @result = begin; a << l ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save4
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a << text ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_RawNoteBlock unless _tmp
+ return _tmp
+ end
+
+ # CodeFence = &{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!"`" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }
+ def _CodeFence
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; github? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+
+ _save3 = self.pos
+ while true # sequence
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save3
+ break
+ end
+ _tmp = apply(:_StrChunk)
+ format = @result
+ unless _tmp
+ self.pos = _save3
+ end
+ break
+ end # end sequence
+
+ unless _tmp
+ _tmp = true
+ self.pos = _save2
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Spnl)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _save4 = self.pos
+
+ _save5 = self.pos
+ while true # choice
+ _save6 = self.pos
+
+ _save7 = self.pos
+ while true # sequence
+ _save8 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save9 = self.pos
+ while true # sequence
+ _save10 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save10
+ unless _tmp
+ self.pos = _save9
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save9
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save6
+ end
+ break if _tmp
+ self.pos = _save5
+
+ _save11 = self.pos
+ while true # sequence
+ _save12 = self.pos
+ _tmp = apply(:_Ticks3)
+ _tmp = _tmp ? nil : true
+ self.pos = _save12
+ unless _tmp
+ self.pos = _save11
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save11
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save5
+ _tmp = apply(:_Spacechar)
+ break if _tmp
+ self.pos = _save5
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save5
+ break
+ end # end choice
+
+ if _tmp
+ while true
+
+ _save13 = self.pos
+ while true # choice
+ _save14 = self.pos
+
+ _save15 = self.pos
+ while true # sequence
+ _save16 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save16
+ unless _tmp
+ self.pos = _save15
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save15
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save17 = self.pos
+ while true # sequence
+ _save18 = self.pos
+ _tmp = match_string("`")
+ _tmp = _tmp ? nil : true
+ self.pos = _save18
+ unless _tmp
+ self.pos = _save17
+ break
+ end
+ _tmp = apply(:_Nonspacechar)
+ unless _tmp
+ self.pos = _save17
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save14
+ end
+ break if _tmp
+ self.pos = _save13
+
+ _save19 = self.pos
+ while true # sequence
+ _save20 = self.pos
+ _tmp = apply(:_Ticks3)
+ _tmp = _tmp ? nil : true
+ self.pos = _save20
+ unless _tmp
+ self.pos = _save19
+ break
+ end
+ _tmp = scan(/\A(?-mix:`+)/)
+ unless _tmp
+ self.pos = _save19
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save13
+ _tmp = apply(:_Spacechar)
+ break if _tmp
+ self.pos = _save13
+ _tmp = _Newline()
+ break if _tmp
+ self.pos = _save13
+ break
+ end # end choice
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save4
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ while true
+ _tmp = _Newline()
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; verbatim = RDoc::Markup::Verbatim.new text
+ verbatim.format = format.intern if format.instance_of?(String)
+ verbatim
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_CodeFence unless _tmp
+ return _tmp
+ end
+
+ # DefinitionList = &{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }
+ def _DefinitionList
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; definition_lists? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _ary = []
+ _tmp = apply(:_DefinitionListItem)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_DefinitionListItem)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save2
+ end
+ list = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; RDoc::Markup::List.new :NOTE, *list.flatten ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_DefinitionList unless _tmp
+ return _tmp
+ end
+
+ # DefinitionListItem = DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }
+ def _DefinitionListItem
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_DefinitionListLabel)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_DefinitionListLabel)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ label = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _ary = []
+ _tmp = apply(:_DefinitionListDefinition)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_DefinitionListDefinition)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save2
+ end
+ defns = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; list_items = []
+ list_items <<
+ RDoc::Markup::ListItem.new(label, defns.shift)
+
+ list_items.concat defns.map { |defn|
+ RDoc::Markup::ListItem.new nil, defn
+ } unless list_items.empty?
+
+ list_items
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_DefinitionListItem unless _tmp
+ return _tmp
+ end
+
+ # DefinitionListLabel = StrChunk:label @Sp @Newline { label }
+ def _DefinitionListLabel
+
+ _save = self.pos
+ while true # sequence
+ _tmp = apply(:_StrChunk)
+ label = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Sp()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; label ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_DefinitionListLabel unless _tmp
+ return _tmp
+ end
+
+ # DefinitionListDefinition = @NonindentSpace ":" @Space Inlines:a @BlankLine+ { paragraph a }
+ def _DefinitionListDefinition
+
+ _save = self.pos
+ while true # sequence
+ _tmp = _NonindentSpace()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string(":")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Space()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_Inlines)
+ a = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save1 = self.pos
+ _tmp = _BlankLine()
+ if _tmp
+ while true
+ _tmp = _BlankLine()
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; paragraph a ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_DefinitionListDefinition unless _tmp
+ return _tmp
+ end
+
+ Rules = {}
+ Rules[:_root] = rule_info("root", "Doc")
+ Rules[:_Doc] = rule_info("Doc", "BOM? Block*:a { RDoc::Markup::Document.new(*a.compact) }")
+ Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)")
+ Rules[:_Para] = rule_info("Para", "@NonindentSpace Inlines:a @BlankLine+ { paragraph a }")
+ Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }")
+ Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline")
+ Rules[:_AtxStart] = rule_info("AtxStart", "< /\\\#{1,6}/ > { text.length }")
+ Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Sp AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }")
+ Rules[:_SetextHeading] = rule_info("SetextHeading", "(SetextHeading1 | SetextHeading2)")
+ Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={1,}/ @Newline")
+ Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{1,}/ @Newline")
+ Rules[:_SetextHeading1] = rule_info("SetextHeading1", "&(@RawLine SetextBottom1) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom1 { RDoc::Markup::Heading.new(1, a.join) }")
+ Rules[:_SetextHeading2] = rule_info("SetextHeading2", "&(@RawLine SetextBottom2) @StartList:a (!@Endline Inline:b { a << b })+ @Sp @Newline SetextBottom2 { RDoc::Markup::Heading.new(2, a.join) }")
+ Rules[:_Heading] = rule_info("Heading", "(SetextHeading | AtxHeading)")
+ Rules[:_BlockQuote] = rule_info("BlockQuote", "BlockQuoteRaw:a { RDoc::Markup::BlockQuote.new(*a) }")
+ Rules[:_BlockQuoteRaw] = rule_info("BlockQuoteRaw", "@StartList:a (\">\" \" \"? Line:l { a << l } (!\">\" !@BlankLine Line:c { a << c })* (@BlankLine:n { a << n })*)+ { inner_parse a.join }")
+ Rules[:_NonblankIndentedLine] = rule_info("NonblankIndentedLine", "!@BlankLine IndentedLine")
+ Rules[:_VerbatimChunk] = rule_info("VerbatimChunk", "@BlankLine*:a NonblankIndentedLine+:b { a.concat b }")
+ Rules[:_Verbatim] = rule_info("Verbatim", "VerbatimChunk+:a { RDoc::Markup::Verbatim.new(*a.flatten) }")
+ Rules[:_HorizontalRule] = rule_info("HorizontalRule", "@NonindentSpace (\"*\" @Sp \"*\" @Sp \"*\" (@Sp \"*\")* | \"-\" @Sp \"-\" @Sp \"-\" (@Sp \"-\")* | \"_\" @Sp \"_\" @Sp \"_\" (@Sp \"_\")*) @Sp @Newline @BlankLine+ { RDoc::Markup::Rule.new 1 }")
+ Rules[:_Bullet] = rule_info("Bullet", "!HorizontalRule @NonindentSpace /[+*-]/ @Spacechar+")
+ Rules[:_BulletList] = rule_info("BulletList", "&Bullet (ListTight | ListLoose):a { RDoc::Markup::List.new(:BULLET, *a) }")
+ Rules[:_ListTight] = rule_info("ListTight", "ListItemTight+:a @BlankLine* !(Bullet | Enumerator) { a }")
+ Rules[:_ListLoose] = rule_info("ListLoose", "@StartList:a (ListItem:b @BlankLine* { a << b })+ { a }")
+ Rules[:_ListItem] = rule_info("ListItem", "(Bullet | Enumerator) @StartList:a ListBlock:b { a << b } (ListContinuationBlock:c { a.push(*c) })* { list_item_from a }")
+ Rules[:_ListItemTight] = rule_info("ListItemTight", "(Bullet | Enumerator) ListBlock:a (!@BlankLine ListContinuationBlock:b { a.push(*b) })* !ListContinuationBlock { list_item_from a }")
+ Rules[:_ListBlock] = rule_info("ListBlock", "!@BlankLine Line:a ListBlockLine*:c { [a, *c] }")
+ Rules[:_ListContinuationBlock] = rule_info("ListContinuationBlock", "@StartList:a @BlankLine* { a << \"\\n\" } (Indent ListBlock:b { a.concat b })+ { a }")
+ Rules[:_Enumerator] = rule_info("Enumerator", "@NonindentSpace [0-9]+ \".\" @Spacechar+")
+ Rules[:_OrderedList] = rule_info("OrderedList", "&Enumerator (ListTight | ListLoose):a { RDoc::Markup::List.new(:NUMBER, *a) }")
+ Rules[:_ListBlockLine] = rule_info("ListBlockLine", "!@BlankLine !(Indent? (Bullet | Enumerator)) !HorizontalRule OptionallyIndentedLine")
+ Rules[:_HtmlOpenAnchor] = rule_info("HtmlOpenAnchor", "\"<\" Spnl (\"a\" | \"A\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlCloseAnchor] = rule_info("HtmlCloseAnchor", "\"<\" Spnl \"/\" (\"a\" | \"A\") Spnl \">\"")
+ Rules[:_HtmlAnchor] = rule_info("HtmlAnchor", "HtmlOpenAnchor (HtmlAnchor | !HtmlCloseAnchor .)* HtmlCloseAnchor")
+ Rules[:_HtmlBlockOpenAddress] = rule_info("HtmlBlockOpenAddress", "\"<\" Spnl (\"address\" | \"ADDRESS\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseAddress] = rule_info("HtmlBlockCloseAddress", "\"<\" Spnl \"/\" (\"address\" | \"ADDRESS\") Spnl \">\"")
+ Rules[:_HtmlBlockAddress] = rule_info("HtmlBlockAddress", "HtmlBlockOpenAddress (HtmlBlockAddress | !HtmlBlockCloseAddress .)* HtmlBlockCloseAddress")
+ Rules[:_HtmlBlockOpenBlockquote] = rule_info("HtmlBlockOpenBlockquote", "\"<\" Spnl (\"blockquote\" | \"BLOCKQUOTE\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseBlockquote] = rule_info("HtmlBlockCloseBlockquote", "\"<\" Spnl \"/\" (\"blockquote\" | \"BLOCKQUOTE\") Spnl \">\"")
+ Rules[:_HtmlBlockBlockquote] = rule_info("HtmlBlockBlockquote", "HtmlBlockOpenBlockquote (HtmlBlockBlockquote | !HtmlBlockCloseBlockquote .)* HtmlBlockCloseBlockquote")
+ Rules[:_HtmlBlockOpenCenter] = rule_info("HtmlBlockOpenCenter", "\"<\" Spnl (\"center\" | \"CENTER\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseCenter] = rule_info("HtmlBlockCloseCenter", "\"<\" Spnl \"/\" (\"center\" | \"CENTER\") Spnl \">\"")
+ Rules[:_HtmlBlockCenter] = rule_info("HtmlBlockCenter", "HtmlBlockOpenCenter (HtmlBlockCenter | !HtmlBlockCloseCenter .)* HtmlBlockCloseCenter")
+ Rules[:_HtmlBlockOpenDir] = rule_info("HtmlBlockOpenDir", "\"<\" Spnl (\"dir\" | \"DIR\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseDir] = rule_info("HtmlBlockCloseDir", "\"<\" Spnl \"/\" (\"dir\" | \"DIR\") Spnl \">\"")
+ Rules[:_HtmlBlockDir] = rule_info("HtmlBlockDir", "HtmlBlockOpenDir (HtmlBlockDir | !HtmlBlockCloseDir .)* HtmlBlockCloseDir")
+ Rules[:_HtmlBlockOpenDiv] = rule_info("HtmlBlockOpenDiv", "\"<\" Spnl (\"div\" | \"DIV\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseDiv] = rule_info("HtmlBlockCloseDiv", "\"<\" Spnl \"/\" (\"div\" | \"DIV\") Spnl \">\"")
+ Rules[:_HtmlBlockDiv] = rule_info("HtmlBlockDiv", "HtmlBlockOpenDiv (HtmlBlockDiv | !HtmlBlockCloseDiv .)* HtmlBlockCloseDiv")
+ Rules[:_HtmlBlockOpenDl] = rule_info("HtmlBlockOpenDl", "\"<\" Spnl (\"dl\" | \"DL\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseDl] = rule_info("HtmlBlockCloseDl", "\"<\" Spnl \"/\" (\"dl\" | \"DL\") Spnl \">\"")
+ Rules[:_HtmlBlockDl] = rule_info("HtmlBlockDl", "HtmlBlockOpenDl (HtmlBlockDl | !HtmlBlockCloseDl .)* HtmlBlockCloseDl")
+ Rules[:_HtmlBlockOpenFieldset] = rule_info("HtmlBlockOpenFieldset", "\"<\" Spnl (\"fieldset\" | \"FIELDSET\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseFieldset] = rule_info("HtmlBlockCloseFieldset", "\"<\" Spnl \"/\" (\"fieldset\" | \"FIELDSET\") Spnl \">\"")
+ Rules[:_HtmlBlockFieldset] = rule_info("HtmlBlockFieldset", "HtmlBlockOpenFieldset (HtmlBlockFieldset | !HtmlBlockCloseFieldset .)* HtmlBlockCloseFieldset")
+ Rules[:_HtmlBlockOpenForm] = rule_info("HtmlBlockOpenForm", "\"<\" Spnl (\"form\" | \"FORM\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseForm] = rule_info("HtmlBlockCloseForm", "\"<\" Spnl \"/\" (\"form\" | \"FORM\") Spnl \">\"")
+ Rules[:_HtmlBlockForm] = rule_info("HtmlBlockForm", "HtmlBlockOpenForm (HtmlBlockForm | !HtmlBlockCloseForm .)* HtmlBlockCloseForm")
+ Rules[:_HtmlBlockOpenH1] = rule_info("HtmlBlockOpenH1", "\"<\" Spnl (\"h1\" | \"H1\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH1] = rule_info("HtmlBlockCloseH1", "\"<\" Spnl \"/\" (\"h1\" | \"H1\") Spnl \">\"")
+ Rules[:_HtmlBlockH1] = rule_info("HtmlBlockH1", "HtmlBlockOpenH1 (HtmlBlockH1 | !HtmlBlockCloseH1 .)* HtmlBlockCloseH1")
+ Rules[:_HtmlBlockOpenH2] = rule_info("HtmlBlockOpenH2", "\"<\" Spnl (\"h2\" | \"H2\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH2] = rule_info("HtmlBlockCloseH2", "\"<\" Spnl \"/\" (\"h2\" | \"H2\") Spnl \">\"")
+ Rules[:_HtmlBlockH2] = rule_info("HtmlBlockH2", "HtmlBlockOpenH2 (HtmlBlockH2 | !HtmlBlockCloseH2 .)* HtmlBlockCloseH2")
+ Rules[:_HtmlBlockOpenH3] = rule_info("HtmlBlockOpenH3", "\"<\" Spnl (\"h3\" | \"H3\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH3] = rule_info("HtmlBlockCloseH3", "\"<\" Spnl \"/\" (\"h3\" | \"H3\") Spnl \">\"")
+ Rules[:_HtmlBlockH3] = rule_info("HtmlBlockH3", "HtmlBlockOpenH3 (HtmlBlockH3 | !HtmlBlockCloseH3 .)* HtmlBlockCloseH3")
+ Rules[:_HtmlBlockOpenH4] = rule_info("HtmlBlockOpenH4", "\"<\" Spnl (\"h4\" | \"H4\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH4] = rule_info("HtmlBlockCloseH4", "\"<\" Spnl \"/\" (\"h4\" | \"H4\") Spnl \">\"")
+ Rules[:_HtmlBlockH4] = rule_info("HtmlBlockH4", "HtmlBlockOpenH4 (HtmlBlockH4 | !HtmlBlockCloseH4 .)* HtmlBlockCloseH4")
+ Rules[:_HtmlBlockOpenH5] = rule_info("HtmlBlockOpenH5", "\"<\" Spnl (\"h5\" | \"H5\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH5] = rule_info("HtmlBlockCloseH5", "\"<\" Spnl \"/\" (\"h5\" | \"H5\") Spnl \">\"")
+ Rules[:_HtmlBlockH5] = rule_info("HtmlBlockH5", "HtmlBlockOpenH5 (HtmlBlockH5 | !HtmlBlockCloseH5 .)* HtmlBlockCloseH5")
+ Rules[:_HtmlBlockOpenH6] = rule_info("HtmlBlockOpenH6", "\"<\" Spnl (\"h6\" | \"H6\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseH6] = rule_info("HtmlBlockCloseH6", "\"<\" Spnl \"/\" (\"h6\" | \"H6\") Spnl \">\"")
+ Rules[:_HtmlBlockH6] = rule_info("HtmlBlockH6", "HtmlBlockOpenH6 (HtmlBlockH6 | !HtmlBlockCloseH6 .)* HtmlBlockCloseH6")
+ Rules[:_HtmlBlockOpenMenu] = rule_info("HtmlBlockOpenMenu", "\"<\" Spnl (\"menu\" | \"MENU\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseMenu] = rule_info("HtmlBlockCloseMenu", "\"<\" Spnl \"/\" (\"menu\" | \"MENU\") Spnl \">\"")
+ Rules[:_HtmlBlockMenu] = rule_info("HtmlBlockMenu", "HtmlBlockOpenMenu (HtmlBlockMenu | !HtmlBlockCloseMenu .)* HtmlBlockCloseMenu")
+ Rules[:_HtmlBlockOpenNoframes] = rule_info("HtmlBlockOpenNoframes", "\"<\" Spnl (\"noframes\" | \"NOFRAMES\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseNoframes] = rule_info("HtmlBlockCloseNoframes", "\"<\" Spnl \"/\" (\"noframes\" | \"NOFRAMES\") Spnl \">\"")
+ Rules[:_HtmlBlockNoframes] = rule_info("HtmlBlockNoframes", "HtmlBlockOpenNoframes (HtmlBlockNoframes | !HtmlBlockCloseNoframes .)* HtmlBlockCloseNoframes")
+ Rules[:_HtmlBlockOpenNoscript] = rule_info("HtmlBlockOpenNoscript", "\"<\" Spnl (\"noscript\" | \"NOSCRIPT\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseNoscript] = rule_info("HtmlBlockCloseNoscript", "\"<\" Spnl \"/\" (\"noscript\" | \"NOSCRIPT\") Spnl \">\"")
+ Rules[:_HtmlBlockNoscript] = rule_info("HtmlBlockNoscript", "HtmlBlockOpenNoscript (HtmlBlockNoscript | !HtmlBlockCloseNoscript .)* HtmlBlockCloseNoscript")
+ Rules[:_HtmlBlockOpenOl] = rule_info("HtmlBlockOpenOl", "\"<\" Spnl (\"ol\" | \"OL\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseOl] = rule_info("HtmlBlockCloseOl", "\"<\" Spnl \"/\" (\"ol\" | \"OL\") Spnl \">\"")
+ Rules[:_HtmlBlockOl] = rule_info("HtmlBlockOl", "HtmlBlockOpenOl (HtmlBlockOl | !HtmlBlockCloseOl .)* HtmlBlockCloseOl")
+ Rules[:_HtmlBlockOpenP] = rule_info("HtmlBlockOpenP", "\"<\" Spnl (\"p\" | \"P\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseP] = rule_info("HtmlBlockCloseP", "\"<\" Spnl \"/\" (\"p\" | \"P\") Spnl \">\"")
+ Rules[:_HtmlBlockP] = rule_info("HtmlBlockP", "HtmlBlockOpenP (HtmlBlockP | !HtmlBlockCloseP .)* HtmlBlockCloseP")
+ Rules[:_HtmlBlockOpenPre] = rule_info("HtmlBlockOpenPre", "\"<\" Spnl (\"pre\" | \"PRE\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockClosePre] = rule_info("HtmlBlockClosePre", "\"<\" Spnl \"/\" (\"pre\" | \"PRE\") Spnl \">\"")
+ Rules[:_HtmlBlockPre] = rule_info("HtmlBlockPre", "HtmlBlockOpenPre (HtmlBlockPre | !HtmlBlockClosePre .)* HtmlBlockClosePre")
+ Rules[:_HtmlBlockOpenTable] = rule_info("HtmlBlockOpenTable", "\"<\" Spnl (\"table\" | \"TABLE\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTable] = rule_info("HtmlBlockCloseTable", "\"<\" Spnl \"/\" (\"table\" | \"TABLE\") Spnl \">\"")
+ Rules[:_HtmlBlockTable] = rule_info("HtmlBlockTable", "HtmlBlockOpenTable (HtmlBlockTable | !HtmlBlockCloseTable .)* HtmlBlockCloseTable")
+ Rules[:_HtmlBlockOpenUl] = rule_info("HtmlBlockOpenUl", "\"<\" Spnl (\"ul\" | \"UL\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseUl] = rule_info("HtmlBlockCloseUl", "\"<\" Spnl \"/\" (\"ul\" | \"UL\") Spnl \">\"")
+ Rules[:_HtmlBlockUl] = rule_info("HtmlBlockUl", "HtmlBlockOpenUl (HtmlBlockUl | !HtmlBlockCloseUl .)* HtmlBlockCloseUl")
+ Rules[:_HtmlBlockOpenDd] = rule_info("HtmlBlockOpenDd", "\"<\" Spnl (\"dd\" | \"DD\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseDd] = rule_info("HtmlBlockCloseDd", "\"<\" Spnl \"/\" (\"dd\" | \"DD\") Spnl \">\"")
+ Rules[:_HtmlBlockDd] = rule_info("HtmlBlockDd", "HtmlBlockOpenDd (HtmlBlockDd | !HtmlBlockCloseDd .)* HtmlBlockCloseDd")
+ Rules[:_HtmlBlockOpenDt] = rule_info("HtmlBlockOpenDt", "\"<\" Spnl (\"dt\" | \"DT\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseDt] = rule_info("HtmlBlockCloseDt", "\"<\" Spnl \"/\" (\"dt\" | \"DT\") Spnl \">\"")
+ Rules[:_HtmlBlockDt] = rule_info("HtmlBlockDt", "HtmlBlockOpenDt (HtmlBlockDt | !HtmlBlockCloseDt .)* HtmlBlockCloseDt")
+ Rules[:_HtmlBlockOpenFrameset] = rule_info("HtmlBlockOpenFrameset", "\"<\" Spnl (\"frameset\" | \"FRAMESET\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseFrameset] = rule_info("HtmlBlockCloseFrameset", "\"<\" Spnl \"/\" (\"frameset\" | \"FRAMESET\") Spnl \">\"")
+ Rules[:_HtmlBlockFrameset] = rule_info("HtmlBlockFrameset", "HtmlBlockOpenFrameset (HtmlBlockFrameset | !HtmlBlockCloseFrameset .)* HtmlBlockCloseFrameset")
+ Rules[:_HtmlBlockOpenLi] = rule_info("HtmlBlockOpenLi", "\"<\" Spnl (\"li\" | \"LI\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseLi] = rule_info("HtmlBlockCloseLi", "\"<\" Spnl \"/\" (\"li\" | \"LI\") Spnl \">\"")
+ Rules[:_HtmlBlockLi] = rule_info("HtmlBlockLi", "HtmlBlockOpenLi (HtmlBlockLi | !HtmlBlockCloseLi .)* HtmlBlockCloseLi")
+ Rules[:_HtmlBlockOpenTbody] = rule_info("HtmlBlockOpenTbody", "\"<\" Spnl (\"tbody\" | \"TBODY\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTbody] = rule_info("HtmlBlockCloseTbody", "\"<\" Spnl \"/\" (\"tbody\" | \"TBODY\") Spnl \">\"")
+ Rules[:_HtmlBlockTbody] = rule_info("HtmlBlockTbody", "HtmlBlockOpenTbody (HtmlBlockTbody | !HtmlBlockCloseTbody .)* HtmlBlockCloseTbody")
+ Rules[:_HtmlBlockOpenTd] = rule_info("HtmlBlockOpenTd", "\"<\" Spnl (\"td\" | \"TD\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTd] = rule_info("HtmlBlockCloseTd", "\"<\" Spnl \"/\" (\"td\" | \"TD\") Spnl \">\"")
+ Rules[:_HtmlBlockTd] = rule_info("HtmlBlockTd", "HtmlBlockOpenTd (HtmlBlockTd | !HtmlBlockCloseTd .)* HtmlBlockCloseTd")
+ Rules[:_HtmlBlockOpenTfoot] = rule_info("HtmlBlockOpenTfoot", "\"<\" Spnl (\"tfoot\" | \"TFOOT\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTfoot] = rule_info("HtmlBlockCloseTfoot", "\"<\" Spnl \"/\" (\"tfoot\" | \"TFOOT\") Spnl \">\"")
+ Rules[:_HtmlBlockTfoot] = rule_info("HtmlBlockTfoot", "HtmlBlockOpenTfoot (HtmlBlockTfoot | !HtmlBlockCloseTfoot .)* HtmlBlockCloseTfoot")
+ Rules[:_HtmlBlockOpenTh] = rule_info("HtmlBlockOpenTh", "\"<\" Spnl (\"th\" | \"TH\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTh] = rule_info("HtmlBlockCloseTh", "\"<\" Spnl \"/\" (\"th\" | \"TH\") Spnl \">\"")
+ Rules[:_HtmlBlockTh] = rule_info("HtmlBlockTh", "HtmlBlockOpenTh (HtmlBlockTh | !HtmlBlockCloseTh .)* HtmlBlockCloseTh")
+ Rules[:_HtmlBlockOpenThead] = rule_info("HtmlBlockOpenThead", "\"<\" Spnl (\"thead\" | \"THEAD\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseThead] = rule_info("HtmlBlockCloseThead", "\"<\" Spnl \"/\" (\"thead\" | \"THEAD\") Spnl \">\"")
+ Rules[:_HtmlBlockThead] = rule_info("HtmlBlockThead", "HtmlBlockOpenThead (HtmlBlockThead | !HtmlBlockCloseThead .)* HtmlBlockCloseThead")
+ Rules[:_HtmlBlockOpenTr] = rule_info("HtmlBlockOpenTr", "\"<\" Spnl (\"tr\" | \"TR\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseTr] = rule_info("HtmlBlockCloseTr", "\"<\" Spnl \"/\" (\"tr\" | \"TR\") Spnl \">\"")
+ Rules[:_HtmlBlockTr] = rule_info("HtmlBlockTr", "HtmlBlockOpenTr (HtmlBlockTr | !HtmlBlockCloseTr .)* HtmlBlockCloseTr")
+ Rules[:_HtmlBlockOpenScript] = rule_info("HtmlBlockOpenScript", "\"<\" Spnl (\"script\" | \"SCRIPT\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseScript] = rule_info("HtmlBlockCloseScript", "\"<\" Spnl \"/\" (\"script\" | \"SCRIPT\") Spnl \">\"")
+ Rules[:_HtmlBlockScript] = rule_info("HtmlBlockScript", "HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript")
+ Rules[:_HtmlBlockOpenHead] = rule_info("HtmlBlockOpenHead", "\"<\" Spnl (\"head\" | \"HEAD\") Spnl HtmlAttribute* \">\"")
+ Rules[:_HtmlBlockCloseHead] = rule_info("HtmlBlockCloseHead", "\"<\" Spnl \"/\" (\"head\" | \"HEAD\") Spnl \">\"")
+ Rules[:_HtmlBlockHead] = rule_info("HtmlBlockHead", "HtmlBlockOpenHead (!HtmlBlockCloseHead .)* HtmlBlockCloseHead")
+ Rules[:_HtmlBlockInTags] = rule_info("HtmlBlockInTags", "(HtmlAnchor | HtmlBlockAddress | HtmlBlockBlockquote | HtmlBlockCenter | HtmlBlockDir | HtmlBlockDiv | HtmlBlockDl | HtmlBlockFieldset | HtmlBlockForm | HtmlBlockH1 | HtmlBlockH2 | HtmlBlockH3 | HtmlBlockH4 | HtmlBlockH5 | HtmlBlockH6 | HtmlBlockMenu | HtmlBlockNoframes | HtmlBlockNoscript | HtmlBlockOl | HtmlBlockP | HtmlBlockPre | HtmlBlockTable | HtmlBlockUl | HtmlBlockDd | HtmlBlockDt | HtmlBlockFrameset | HtmlBlockLi | HtmlBlockTbody | HtmlBlockTd | HtmlBlockTfoot | HtmlBlockTh | HtmlBlockThead | HtmlBlockTr | HtmlBlockScript | HtmlBlockHead)")
+ Rules[:_HtmlBlock] = rule_info("HtmlBlock", "< (HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing | HtmlUnclosed) > @BlankLine+ { if html? then RDoc::Markup::Raw.new text end }")
+ Rules[:_HtmlUnclosed] = rule_info("HtmlUnclosed", "\"<\" Spnl HtmlUnclosedType Spnl HtmlAttribute* Spnl \">\"")
+ Rules[:_HtmlUnclosedType] = rule_info("HtmlUnclosedType", "(\"HR\" | \"hr\")")
+ Rules[:_HtmlBlockSelfClosing] = rule_info("HtmlBlockSelfClosing", "\"<\" Spnl HtmlBlockType Spnl HtmlAttribute* \"/\" Spnl \">\"")
+ Rules[:_HtmlBlockType] = rule_info("HtmlBlockType", "(\"ADDRESS\" | \"BLOCKQUOTE\" | \"CENTER\" | \"DD\" | \"DIR\" | \"DIV\" | \"DL\" | \"DT\" | \"FIELDSET\" | \"FORM\" | \"FRAMESET\" | \"H1\" | \"H2\" | \"H3\" | \"H4\" | \"H5\" | \"H6\" | \"HR\" | \"ISINDEX\" | \"LI\" | \"MENU\" | \"NOFRAMES\" | \"NOSCRIPT\" | \"OL\" | \"P\" | \"PRE\" | \"SCRIPT\" | \"TABLE\" | \"TBODY\" | \"TD\" | \"TFOOT\" | \"TH\" | \"THEAD\" | \"TR\" | \"UL\" | \"address\" | \"blockquote\" | \"center\" | \"dd\" | \"dir\" | \"div\" | \"dl\" | \"dt\" | \"fieldset\" | \"form\" | \"frameset\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"hr\" | \"isindex\" | \"li\" | \"menu\" | \"noframes\" | \"noscript\" | \"ol\" | \"p\" | \"pre\" | \"script\" | \"table\" | \"tbody\" | \"td\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"ul\")")
+ Rules[:_StyleOpen] = rule_info("StyleOpen", "\"<\" Spnl (\"style\" | \"STYLE\") Spnl HtmlAttribute* \">\"")
+ Rules[:_StyleClose] = rule_info("StyleClose", "\"<\" Spnl \"/\" (\"style\" | \"STYLE\") Spnl \">\"")
+ Rules[:_InStyleTags] = rule_info("InStyleTags", "StyleOpen (!StyleClose .)* StyleClose")
+ Rules[:_StyleBlock] = rule_info("StyleBlock", "< InStyleTags > @BlankLine* { if css? then RDoc::Markup::Raw.new text end }")
+ Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }")
+ Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)")
+ Rules[:_Space] = rule_info("Space", "@Spacechar+ { \" \" }")
+ Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }")
+ Rules[:_StrChunk] = rule_info("StrChunk", "< (@NormalChar | /_+/ &Alphanumeric)+ > { text }")
+ Rules[:_EscapedChar] = rule_info("EscapedChar", "\"\\\\\" !@Newline < /[:\\\\`|*_{}\\[\\]()\#+.!><-]/ > { text }")
+ Rules[:_Entity] = rule_info("Entity", "(HexEntity | DecEntity | CharEntity):a { a }")
+ Rules[:_Endline] = rule_info("Endline", "(@LineBreak | @TerminalEndline | @NormalEndline)")
+ Rules[:_NormalEndline] = rule_info("NormalEndline", "@Sp @Newline !@BlankLine !\">\" !AtxStart !(Line /={1,}|-{1,}/ @Newline) { \"\\n\" }")
+ Rules[:_TerminalEndline] = rule_info("TerminalEndline", "@Sp @Newline @Eof")
+ Rules[:_LineBreak] = rule_info("LineBreak", "\" \" @NormalEndline { RDoc::Markup::HardBreak.new }")
+ Rules[:_Symbol] = rule_info("Symbol", "< @SpecialChar > { text }")
+ Rules[:_UlOrStarLine] = rule_info("UlOrStarLine", "(UlLine | StarLine):a { a }")
+ Rules[:_StarLine] = rule_info("StarLine", "(< /\\*{4,}/ > { text } | < @Spacechar /\\*+/ &@Spacechar > { text })")
+ Rules[:_UlLine] = rule_info("UlLine", "(< /_{4,}/ > { text } | < @Spacechar /_+/ &@Spacechar > { text })")
+ Rules[:_Emph] = rule_info("Emph", "(EmphStar | EmphUl)")
+ Rules[:_Whitespace] = rule_info("Whitespace", "(@Spacechar | @Newline)")
+ Rules[:_EmphStar] = rule_info("EmphStar", "\"*\" !@Whitespace @StartList:a (!\"*\" Inline:b { a << b } | StrongStar:b { a << b })+ \"*\" { emphasis a.join }")
+ Rules[:_EmphUl] = rule_info("EmphUl", "\"_\" !@Whitespace @StartList:a (!\"_\" Inline:b { a << b } | StrongUl:b { a << b })+ \"_\" { emphasis a.join }")
+ Rules[:_Strong] = rule_info("Strong", "(StrongStar | StrongUl)")
+ Rules[:_StrongStar] = rule_info("StrongStar", "\"**\" !@Whitespace @StartList:a (!\"**\" Inline:b { a << b })+ \"**\" { strong a.join }")
+ Rules[:_StrongUl] = rule_info("StrongUl", "\"__\" !@Whitespace @StartList:a (!\"__\" Inline:b { a << b })+ \"__\" { strong a.join }")
+ Rules[:_Strike] = rule_info("Strike", "&{ strike? } \"~~\" !@Whitespace @StartList:a (!\"~~\" Inline:b { a << b })+ \"~~\" { strike a.join }")
+ Rules[:_Image] = rule_info("Image", "\"!\" (ExplicitLink | ReferenceLink):a { \"rdoc-image:\#{a[/\\[(.*)\\]/, 1]}\" }")
+ Rules[:_Link] = rule_info("Link", "(ExplicitLink | ReferenceLink | AutoLink)")
+ Rules[:_ReferenceLink] = rule_info("ReferenceLink", "(ReferenceLinkDouble | ReferenceLinkSingle)")
+ Rules[:_ReferenceLinkDouble] = rule_info("ReferenceLinkDouble", "Label:content < Spnl > !\"[]\" Label:label { link_to content, label, text }")
+ Rules[:_ReferenceLinkSingle] = rule_info("ReferenceLinkSingle", "Label:content < (Spnl \"[]\")? > { link_to content, content, text }")
+ Rules[:_ExplicitLink] = rule_info("ExplicitLink", "Label:l \"(\" @Sp Source:s Spnl Title @Sp \")\" { \"{\#{l}}[\#{s}]\" }")
+ Rules[:_Source] = rule_info("Source", "(\"<\" < SourceContents > \">\" | < SourceContents >) { text }")
+ Rules[:_SourceContents] = rule_info("SourceContents", "((!\"(\" !\")\" !\">\" Nonspacechar)+ | \"(\" SourceContents \")\")*")
+ Rules[:_Title] = rule_info("Title", "(TitleSingle | TitleDouble | \"\"):a { a }")
+ Rules[:_TitleSingle] = rule_info("TitleSingle", "\"'\" (!(\"'\" @Sp (\")\" | @Newline)) .)* \"'\"")
+ Rules[:_TitleDouble] = rule_info("TitleDouble", "\"\\\"\" (!(\"\\\"\" @Sp (\")\" | @Newline)) .)* \"\\\"\"")
+ Rules[:_AutoLink] = rule_info("AutoLink", "(AutoLinkUrl | AutoLinkEmail)")
+ Rules[:_AutoLinkUrl] = rule_info("AutoLinkUrl", "\"<\" < /[A-Za-z]+/ \"://\" (!@Newline !\">\" .)+ > \">\" { text }")
+ Rules[:_AutoLinkEmail] = rule_info("AutoLinkEmail", "\"<\" \"mailto:\"? < /[\\w+.\\/!%~$-]+/i \"@\" (!@Newline !\">\" .)+ > \">\" { \"mailto:\#{text}\" }")
+ Rules[:_Reference] = rule_info("Reference", "@NonindentSpace !\"[]\" Label:label \":\" Spnl RefSrc:link RefTitle @BlankLine+ { \# TODO use title reference label, link nil }")
+ Rules[:_Label] = rule_info("Label", "\"[\" (!\"^\" &{ notes? } | &. &{ !notes? }) @StartList:a (!\"]\" Inline:l { a << l })* \"]\" { a.join.gsub(/\\s+/, ' ') }")
+ Rules[:_RefSrc] = rule_info("RefSrc", "< Nonspacechar+ > { text }")
+ Rules[:_RefTitle] = rule_info("RefTitle", "(RefTitleSingle | RefTitleDouble | RefTitleParens | EmptyTitle)")
+ Rules[:_EmptyTitle] = rule_info("EmptyTitle", "\"\"")
+ Rules[:_RefTitleSingle] = rule_info("RefTitleSingle", "Spnl \"'\" < (!(\"'\" @Sp @Newline | @Newline) .)* > \"'\" { text }")
+ Rules[:_RefTitleDouble] = rule_info("RefTitleDouble", "Spnl \"\\\"\" < (!(\"\\\"\" @Sp @Newline | @Newline) .)* > \"\\\"\" { text }")
+ Rules[:_RefTitleParens] = rule_info("RefTitleParens", "Spnl \"(\" < (!(\")\" @Sp @Newline | @Newline) .)* > \")\" { text }")
+ Rules[:_References] = rule_info("References", "(Reference | SkipBlock)*")
+ Rules[:_Ticks1] = rule_info("Ticks1", "\"`\" !\"`\"")
+ Rules[:_Ticks2] = rule_info("Ticks2", "\"``\" !\"`\"")
+ Rules[:_Ticks3] = rule_info("Ticks3", "\"```\" !\"`\"")
+ Rules[:_Ticks4] = rule_info("Ticks4", "\"````\" !\"`\"")
+ Rules[:_Ticks5] = rule_info("Ticks5", "\"`````\" !\"`\"")
+ Rules[:_Code] = rule_info("Code", "(Ticks1 @Sp < ((!\"`\" Nonspacechar)+ | !Ticks1 /`+/ | !(@Sp Ticks1) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks1 | Ticks2 @Sp < ((!\"`\" Nonspacechar)+ | !Ticks2 /`+/ | !(@Sp Ticks2) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks2 | Ticks3 @Sp < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | !(@Sp Ticks3) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks3 | Ticks4 @Sp < ((!\"`\" Nonspacechar)+ | !Ticks4 /`+/ | !(@Sp Ticks4) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks4 | Ticks5 @Sp < ((!\"`\" Nonspacechar)+ | !Ticks5 /`+/ | !(@Sp Ticks5) (@Spacechar | @Newline !@BlankLine))+ > @Sp Ticks5) { \"<code>\#{text}</code>\" }")
+ Rules[:_RawHtml] = rule_info("RawHtml", "< (HtmlComment | HtmlBlockScript | HtmlTag) > { if html? then text else '' end }")
+ Rules[:_BlankLine] = rule_info("BlankLine", "@Sp @Newline { \"\\n\" }")
+ Rules[:_Quoted] = rule_info("Quoted", "(\"\\\"\" (!\"\\\"\" .)* \"\\\"\" | \"'\" (!\"'\" .)* \"'\")")
+ Rules[:_HtmlAttribute] = rule_info("HtmlAttribute", "(AlphanumericAscii | \"-\")+ Spnl (\"=\" Spnl (Quoted | (!\">\" Nonspacechar)+))? Spnl")
+ Rules[:_HtmlComment] = rule_info("HtmlComment", "\"<!--\" (!\"-->\" .)* \"-->\"")
+ Rules[:_HtmlTag] = rule_info("HtmlTag", "\"<\" Spnl \"/\"? AlphanumericAscii+ Spnl HtmlAttribute* \"/\"? Spnl \">\"")
+ Rules[:_Eof] = rule_info("Eof", "!.")
+ Rules[:_Nonspacechar] = rule_info("Nonspacechar", "!@Spacechar !@Newline .")
+ Rules[:_Sp] = rule_info("Sp", "@Spacechar*")
+ Rules[:_Spnl] = rule_info("Spnl", "@Sp (@Newline @Sp)?")
+ Rules[:_SpecialChar] = rule_info("SpecialChar", "(/[~*_`&\\[\\]()<!\#\\\\'\"]/ | @ExtendedSpecialChar)")
+ Rules[:_NormalChar] = rule_info("NormalChar", "!(@SpecialChar | @Spacechar | @Newline) .")
+ Rules[:_Digit] = rule_info("Digit", "[0-9]")
+ Rules[:_Alphanumeric] = rule_info("Alphanumeric", "%literals.Alphanumeric")
+ Rules[:_AlphanumericAscii] = rule_info("AlphanumericAscii", "%literals.AlphanumericAscii")
+ Rules[:_BOM] = rule_info("BOM", "%literals.BOM")
+ Rules[:_Newline] = rule_info("Newline", "%literals.Newline")
+ Rules[:_Spacechar] = rule_info("Spacechar", "%literals.Spacechar")
+ Rules[:_HexEntity] = rule_info("HexEntity", "/&\#x/i < /[0-9a-fA-F]+/ > \";\" { [text.to_i(16)].pack 'U' }")
+ Rules[:_DecEntity] = rule_info("DecEntity", "\"&\#\" < /[0-9]+/ > \";\" { [text.to_i].pack 'U' }")
+ Rules[:_CharEntity] = rule_info("CharEntity", "\"&\" < /[A-Za-z0-9]+/ > \";\" { if entity = HTML_ENTITIES[text] then entity.pack 'U*' else \"&\#{text};\" end }")
+ Rules[:_NonindentSpace] = rule_info("NonindentSpace", "/ {0,3}/")
+ Rules[:_Indent] = rule_info("Indent", "/\\t| /")
+ Rules[:_IndentedLine] = rule_info("IndentedLine", "Indent Line")
+ Rules[:_OptionallyIndentedLine] = rule_info("OptionallyIndentedLine", "Indent? Line")
+ Rules[:_StartList] = rule_info("StartList", "&. { [] }")
+ Rules[:_Line] = rule_info("Line", "@RawLine:a { a }")
+ Rules[:_RawLine] = rule_info("RawLine", "(< (!\"\\r\" !\"\\n\" .)* @Newline > | < .+ > @Eof) { text }")
+ Rules[:_SkipBlock] = rule_info("SkipBlock", "(HtmlBlock | (!\"\#\" !SetextBottom1 !SetextBottom2 !@BlankLine @RawLine)+ @BlankLine* | @BlankLine+ | @RawLine)")
+ Rules[:_ExtendedSpecialChar] = rule_info("ExtendedSpecialChar", "&{ notes? } \"^\"")
+ Rules[:_NoteReference] = rule_info("NoteReference", "&{ notes? } RawNoteReference:ref { note_for ref }")
+ Rules[:_RawNoteReference] = rule_info("RawNoteReference", "\"[^\" < (!@Newline !\"]\" .)+ > \"]\" { text }")
+ Rules[:_Note] = rule_info("Note", "&{ notes? } @NonindentSpace RawNoteReference:ref \":\" @Sp @StartList:a RawNoteBlock:i { a.concat i } (&Indent RawNoteBlock:i { a.concat i })* { @footnotes[ref] = paragraph a nil }")
+ Rules[:_InlineNote] = rule_info("InlineNote", "&{ notes? } \"^[\" @StartList:a (!\"]\" Inline:l { a << l })+ \"]\" { ref = [:inline, @note_order.length] @footnotes[ref] = paragraph a note_for ref }")
+ Rules[:_Notes] = rule_info("Notes", "(Note | SkipBlock)*")
+ Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }")
+ Rules[:_CodeFence] = rule_info("CodeFence", "&{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }")
+ Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }")
+ Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }")
+ Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }")
+ Rules[:_DefinitionListDefinition] = rule_info("DefinitionListDefinition", "@NonindentSpace \":\" @Space Inlines:a @BlankLine+ { paragraph a }")
+ # :startdoc:
+end
diff --git a/lib/rdoc/markdown/entities.rb b/lib/rdoc/markdown/entities.rb
new file mode 100644
index 0000000000..d2cf610293
--- /dev/null
+++ b/lib/rdoc/markdown/entities.rb
@@ -0,0 +1,2132 @@
+# frozen_string_literal: true
+##
+# HTML entity name map for RDoc::Markdown
+
+RDoc::Markdown::HTML_ENTITIES = {
+ "AElig" => [0x000C6],
+ "AMP" => [0x00026],
+ "Aacute" => [0x000C1],
+ "Abreve" => [0x00102],
+ "Acirc" => [0x000C2],
+ "Acy" => [0x00410],
+ "Afr" => [0x1D504],
+ "Agrave" => [0x000C0],
+ "Alpha" => [0x00391],
+ "Amacr" => [0x00100],
+ "And" => [0x02A53],
+ "Aogon" => [0x00104],
+ "Aopf" => [0x1D538],
+ "ApplyFunction" => [0x02061],
+ "Aring" => [0x000C5],
+ "Ascr" => [0x1D49C],
+ "Assign" => [0x02254],
+ "Atilde" => [0x000C3],
+ "Auml" => [0x000C4],
+ "Backslash" => [0x02216],
+ "Barv" => [0x02AE7],
+ "Barwed" => [0x02306],
+ "Bcy" => [0x00411],
+ "Because" => [0x02235],
+ "Bernoullis" => [0x0212C],
+ "Beta" => [0x00392],
+ "Bfr" => [0x1D505],
+ "Bopf" => [0x1D539],
+ "Breve" => [0x002D8],
+ "Bscr" => [0x0212C],
+ "Bumpeq" => [0x0224E],
+ "CHcy" => [0x00427],
+ "COPY" => [0x000A9],
+ "Cacute" => [0x00106],
+ "Cap" => [0x022D2],
+ "CapitalDifferentialD" => [0x02145],
+ "Cayleys" => [0x0212D],
+ "Ccaron" => [0x0010C],
+ "Ccedil" => [0x000C7],
+ "Ccirc" => [0x00108],
+ "Cconint" => [0x02230],
+ "Cdot" => [0x0010A],
+ "Cedilla" => [0x000B8],
+ "CenterDot" => [0x000B7],
+ "Cfr" => [0x0212D],
+ "Chi" => [0x003A7],
+ "CircleDot" => [0x02299],
+ "CircleMinus" => [0x02296],
+ "CirclePlus" => [0x02295],
+ "CircleTimes" => [0x02297],
+ "ClockwiseContourIntegral" => [0x02232],
+ "CloseCurlyDoubleQuote" => [0x0201D],
+ "CloseCurlyQuote" => [0x02019],
+ "Colon" => [0x02237],
+ "Colone" => [0x02A74],
+ "Congruent" => [0x02261],
+ "Conint" => [0x0222F],
+ "ContourIntegral" => [0x0222E],
+ "Copf" => [0x02102],
+ "Coproduct" => [0x02210],
+ "CounterClockwiseContourIntegral" => [0x02233],
+ "Cross" => [0x02A2F],
+ "Cscr" => [0x1D49E],
+ "Cup" => [0x022D3],
+ "CupCap" => [0x0224D],
+ "DD" => [0x02145],
+ "DDotrahd" => [0x02911],
+ "DJcy" => [0x00402],
+ "DScy" => [0x00405],
+ "DZcy" => [0x0040F],
+ "Dagger" => [0x02021],
+ "Darr" => [0x021A1],
+ "Dashv" => [0x02AE4],
+ "Dcaron" => [0x0010E],
+ "Dcy" => [0x00414],
+ "Del" => [0x02207],
+ "Delta" => [0x00394],
+ "Dfr" => [0x1D507],
+ "DiacriticalAcute" => [0x000B4],
+ "DiacriticalDot" => [0x002D9],
+ "DiacriticalDoubleAcute" => [0x002DD],
+ "DiacriticalGrave" => [0x00060],
+ "DiacriticalTilde" => [0x002DC],
+ "Diamond" => [0x022C4],
+ "DifferentialD" => [0x02146],
+ "Dopf" => [0x1D53B],
+ "Dot" => [0x000A8],
+ "DotDot" => [0x020DC],
+ "DotEqual" => [0x02250],
+ "DoubleContourIntegral" => [0x0222F],
+ "DoubleDot" => [0x000A8],
+ "DoubleDownArrow" => [0x021D3],
+ "DoubleLeftArrow" => [0x021D0],
+ "DoubleLeftRightArrow" => [0x021D4],
+ "DoubleLeftTee" => [0x02AE4],
+ "DoubleLongLeftArrow" => [0x027F8],
+ "DoubleLongLeftRightArrow" => [0x027FA],
+ "DoubleLongRightArrow" => [0x027F9],
+ "DoubleRightArrow" => [0x021D2],
+ "DoubleRightTee" => [0x022A8],
+ "DoubleUpArrow" => [0x021D1],
+ "DoubleUpDownArrow" => [0x021D5],
+ "DoubleVerticalBar" => [0x02225],
+ "DownArrow" => [0x02193],
+ "DownArrowBar" => [0x02913],
+ "DownArrowUpArrow" => [0x021F5],
+ "DownBreve" => [0x00311],
+ "DownLeftRightVector" => [0x02950],
+ "DownLeftTeeVector" => [0x0295E],
+ "DownLeftVector" => [0x021BD],
+ "DownLeftVectorBar" => [0x02956],
+ "DownRightTeeVector" => [0x0295F],
+ "DownRightVector" => [0x021C1],
+ "DownRightVectorBar" => [0x02957],
+ "DownTee" => [0x022A4],
+ "DownTeeArrow" => [0x021A7],
+ "Downarrow" => [0x021D3],
+ "Dscr" => [0x1D49F],
+ "Dstrok" => [0x00110],
+ "ENG" => [0x0014A],
+ "ETH" => [0x000D0],
+ "Eacute" => [0x000C9],
+ "Ecaron" => [0x0011A],
+ "Ecirc" => [0x000CA],
+ "Ecy" => [0x0042D],
+ "Edot" => [0x00116],
+ "Efr" => [0x1D508],
+ "Egrave" => [0x000C8],
+ "Element" => [0x02208],
+ "Emacr" => [0x00112],
+ "EmptySmallSquare" => [0x025FB],
+ "EmptyVerySmallSquare" => [0x025AB],
+ "Eogon" => [0x00118],
+ "Eopf" => [0x1D53C],
+ "Epsilon" => [0x00395],
+ "Equal" => [0x02A75],
+ "EqualTilde" => [0x02242],
+ "Equilibrium" => [0x021CC],
+ "Escr" => [0x02130],
+ "Esim" => [0x02A73],
+ "Eta" => [0x00397],
+ "Euml" => [0x000CB],
+ "Exists" => [0x02203],
+ "ExponentialE" => [0x02147],
+ "Fcy" => [0x00424],
+ "Ffr" => [0x1D509],
+ "FilledSmallSquare" => [0x025FC],
+ "FilledVerySmallSquare" => [0x025AA],
+ "Fopf" => [0x1D53D],
+ "ForAll" => [0x02200],
+ "Fouriertrf" => [0x02131],
+ "Fscr" => [0x02131],
+ "GJcy" => [0x00403],
+ "GT" => [0x0003E],
+ "Gamma" => [0x00393],
+ "Gammad" => [0x003DC],
+ "Gbreve" => [0x0011E],
+ "Gcedil" => [0x00122],
+ "Gcirc" => [0x0011C],
+ "Gcy" => [0x00413],
+ "Gdot" => [0x00120],
+ "Gfr" => [0x1D50A],
+ "Gg" => [0x022D9],
+ "Gopf" => [0x1D53E],
+ "GreaterEqual" => [0x02265],
+ "GreaterEqualLess" => [0x022DB],
+ "GreaterFullEqual" => [0x02267],
+ "GreaterGreater" => [0x02AA2],
+ "GreaterLess" => [0x02277],
+ "GreaterSlantEqual" => [0x02A7E],
+ "GreaterTilde" => [0x02273],
+ "Gscr" => [0x1D4A2],
+ "Gt" => [0x0226B],
+ "HARDcy" => [0x0042A],
+ "Hacek" => [0x002C7],
+ "Hat" => [0x0005E],
+ "Hcirc" => [0x00124],
+ "Hfr" => [0x0210C],
+ "HilbertSpace" => [0x0210B],
+ "Hopf" => [0x0210D],
+ "HorizontalLine" => [0x02500],
+ "Hscr" => [0x0210B],
+ "Hstrok" => [0x00126],
+ "HumpDownHump" => [0x0224E],
+ "HumpEqual" => [0x0224F],
+ "IEcy" => [0x00415],
+ "IJlig" => [0x00132],
+ "IOcy" => [0x00401],
+ "Iacute" => [0x000CD],
+ "Icirc" => [0x000CE],
+ "Icy" => [0x00418],
+ "Idot" => [0x00130],
+ "Ifr" => [0x02111],
+ "Igrave" => [0x000CC],
+ "Im" => [0x02111],
+ "Imacr" => [0x0012A],
+ "ImaginaryI" => [0x02148],
+ "Implies" => [0x021D2],
+ "Int" => [0x0222C],
+ "Integral" => [0x0222B],
+ "Intersection" => [0x022C2],
+ "InvisibleComma" => [0x02063],
+ "InvisibleTimes" => [0x02062],
+ "Iogon" => [0x0012E],
+ "Iopf" => [0x1D540],
+ "Iota" => [0x00399],
+ "Iscr" => [0x02110],
+ "Itilde" => [0x00128],
+ "Iukcy" => [0x00406],
+ "Iuml" => [0x000CF],
+ "Jcirc" => [0x00134],
+ "Jcy" => [0x00419],
+ "Jfr" => [0x1D50D],
+ "Jopf" => [0x1D541],
+ "Jscr" => [0x1D4A5],
+ "Jsercy" => [0x00408],
+ "Jukcy" => [0x00404],
+ "KHcy" => [0x00425],
+ "KJcy" => [0x0040C],
+ "Kappa" => [0x0039A],
+ "Kcedil" => [0x00136],
+ "Kcy" => [0x0041A],
+ "Kfr" => [0x1D50E],
+ "Kopf" => [0x1D542],
+ "Kscr" => [0x1D4A6],
+ "LJcy" => [0x00409],
+ "LT" => [0x0003C],
+ "Lacute" => [0x00139],
+ "Lambda" => [0x0039B],
+ "Lang" => [0x027EA],
+ "Laplacetrf" => [0x02112],
+ "Larr" => [0x0219E],
+ "Lcaron" => [0x0013D],
+ "Lcedil" => [0x0013B],
+ "Lcy" => [0x0041B],
+ "LeftAngleBracket" => [0x027E8],
+ "LeftArrow" => [0x02190],
+ "LeftArrowBar" => [0x021E4],
+ "LeftArrowRightArrow" => [0x021C6],
+ "LeftCeiling" => [0x02308],
+ "LeftDoubleBracket" => [0x027E6],
+ "LeftDownTeeVector" => [0x02961],
+ "LeftDownVector" => [0x021C3],
+ "LeftDownVectorBar" => [0x02959],
+ "LeftFloor" => [0x0230A],
+ "LeftRightArrow" => [0x02194],
+ "LeftRightVector" => [0x0294E],
+ "LeftTee" => [0x022A3],
+ "LeftTeeArrow" => [0x021A4],
+ "LeftTeeVector" => [0x0295A],
+ "LeftTriangle" => [0x022B2],
+ "LeftTriangleBar" => [0x029CF],
+ "LeftTriangleEqual" => [0x022B4],
+ "LeftUpDownVector" => [0x02951],
+ "LeftUpTeeVector" => [0x02960],
+ "LeftUpVector" => [0x021BF],
+ "LeftUpVectorBar" => [0x02958],
+ "LeftVector" => [0x021BC],
+ "LeftVectorBar" => [0x02952],
+ "Leftarrow" => [0x021D0],
+ "Leftrightarrow" => [0x021D4],
+ "LessEqualGreater" => [0x022DA],
+ "LessFullEqual" => [0x02266],
+ "LessGreater" => [0x02276],
+ "LessLess" => [0x02AA1],
+ "LessSlantEqual" => [0x02A7D],
+ "LessTilde" => [0x02272],
+ "Lfr" => [0x1D50F],
+ "Ll" => [0x022D8],
+ "Lleftarrow" => [0x021DA],
+ "Lmidot" => [0x0013F],
+ "LongLeftArrow" => [0x027F5],
+ "LongLeftRightArrow" => [0x027F7],
+ "LongRightArrow" => [0x027F6],
+ "Longleftarrow" => [0x027F8],
+ "Longleftrightarrow" => [0x027FA],
+ "Longrightarrow" => [0x027F9],
+ "Lopf" => [0x1D543],
+ "LowerLeftArrow" => [0x02199],
+ "LowerRightArrow" => [0x02198],
+ "Lscr" => [0x02112],
+ "Lsh" => [0x021B0],
+ "Lstrok" => [0x00141],
+ "Lt" => [0x0226A],
+ "Map" => [0x02905],
+ "Mcy" => [0x0041C],
+ "MediumSpace" => [0x0205F],
+ "Mellintrf" => [0x02133],
+ "Mfr" => [0x1D510],
+ "MinusPlus" => [0x02213],
+ "Mopf" => [0x1D544],
+ "Mscr" => [0x02133],
+ "Mu" => [0x0039C],
+ "NJcy" => [0x0040A],
+ "Nacute" => [0x00143],
+ "Ncaron" => [0x00147],
+ "Ncedil" => [0x00145],
+ "Ncy" => [0x0041D],
+ "NegativeMediumSpace" => [0x0200B],
+ "NegativeThickSpace" => [0x0200B],
+ "NegativeThinSpace" => [0x0200B],
+ "NegativeVeryThinSpace" => [0x0200B],
+ "NestedGreaterGreater" => [0x0226B],
+ "NestedLessLess" => [0x0226A],
+ "NewLine" => [0x0000A],
+ "Nfr" => [0x1D511],
+ "NoBreak" => [0x02060],
+ "NonBreakingSpace" => [0x000A0],
+ "Nopf" => [0x02115],
+ "Not" => [0x02AEC],
+ "NotCongruent" => [0x02262],
+ "NotCupCap" => [0x0226D],
+ "NotDoubleVerticalBar" => [0x02226],
+ "NotElement" => [0x02209],
+ "NotEqual" => [0x02260],
+ "NotEqualTilde" => [0x02242, 0x00338],
+ "NotExists" => [0x02204],
+ "NotGreater" => [0x0226F],
+ "NotGreaterEqual" => [0x02271],
+ "NotGreaterFullEqual" => [0x02267, 0x00338],
+ "NotGreaterGreater" => [0x0226B, 0x00338],
+ "NotGreaterLess" => [0x02279],
+ "NotGreaterSlantEqual" => [0x02A7E, 0x00338],
+ "NotGreaterTilde" => [0x02275],
+ "NotHumpDownHump" => [0x0224E, 0x00338],
+ "NotHumpEqual" => [0x0224F, 0x00338],
+ "NotLeftTriangle" => [0x022EA],
+ "NotLeftTriangleBar" => [0x029CF, 0x00338],
+ "NotLeftTriangleEqual" => [0x022EC],
+ "NotLess" => [0x0226E],
+ "NotLessEqual" => [0x02270],
+ "NotLessGreater" => [0x02278],
+ "NotLessLess" => [0x0226A, 0x00338],
+ "NotLessSlantEqual" => [0x02A7D, 0x00338],
+ "NotLessTilde" => [0x02274],
+ "NotNestedGreaterGreater" => [0x02AA2, 0x00338],
+ "NotNestedLessLess" => [0x02AA1, 0x00338],
+ "NotPrecedes" => [0x02280],
+ "NotPrecedesEqual" => [0x02AAF, 0x00338],
+ "NotPrecedesSlantEqual" => [0x022E0],
+ "NotReverseElement" => [0x0220C],
+ "NotRightTriangle" => [0x022EB],
+ "NotRightTriangleBar" => [0x029D0, 0x00338],
+ "NotRightTriangleEqual" => [0x022ED],
+ "NotSquareSubset" => [0x0228F, 0x00338],
+ "NotSquareSubsetEqual" => [0x022E2],
+ "NotSquareSuperset" => [0x02290, 0x00338],
+ "NotSquareSupersetEqual" => [0x022E3],
+ "NotSubset" => [0x02282, 0x020D2],
+ "NotSubsetEqual" => [0x02288],
+ "NotSucceeds" => [0x02281],
+ "NotSucceedsEqual" => [0x02AB0, 0x00338],
+ "NotSucceedsSlantEqual" => [0x022E1],
+ "NotSucceedsTilde" => [0x0227F, 0x00338],
+ "NotSuperset" => [0x02283, 0x020D2],
+ "NotSupersetEqual" => [0x02289],
+ "NotTilde" => [0x02241],
+ "NotTildeEqual" => [0x02244],
+ "NotTildeFullEqual" => [0x02247],
+ "NotTildeTilde" => [0x02249],
+ "NotVerticalBar" => [0x02224],
+ "Nscr" => [0x1D4A9],
+ "Ntilde" => [0x000D1],
+ "Nu" => [0x0039D],
+ "OElig" => [0x00152],
+ "Oacute" => [0x000D3],
+ "Ocirc" => [0x000D4],
+ "Ocy" => [0x0041E],
+ "Odblac" => [0x00150],
+ "Ofr" => [0x1D512],
+ "Ograve" => [0x000D2],
+ "Omacr" => [0x0014C],
+ "Omega" => [0x003A9],
+ "Omicron" => [0x0039F],
+ "Oopf" => [0x1D546],
+ "OpenCurlyDoubleQuote" => [0x0201C],
+ "OpenCurlyQuote" => [0x02018],
+ "Or" => [0x02A54],
+ "Oscr" => [0x1D4AA],
+ "Oslash" => [0x000D8],
+ "Otilde" => [0x000D5],
+ "Otimes" => [0x02A37],
+ "Ouml" => [0x000D6],
+ "OverBar" => [0x0203E],
+ "OverBrace" => [0x023DE],
+ "OverBracket" => [0x023B4],
+ "OverParenthesis" => [0x023DC],
+ "PartialD" => [0x02202],
+ "Pcy" => [0x0041F],
+ "Pfr" => [0x1D513],
+ "Phi" => [0x003A6],
+ "Pi" => [0x003A0],
+ "PlusMinus" => [0x000B1],
+ "Poincareplane" => [0x0210C],
+ "Popf" => [0x02119],
+ "Pr" => [0x02ABB],
+ "Precedes" => [0x0227A],
+ "PrecedesEqual" => [0x02AAF],
+ "PrecedesSlantEqual" => [0x0227C],
+ "PrecedesTilde" => [0x0227E],
+ "Prime" => [0x02033],
+ "Product" => [0x0220F],
+ "Proportion" => [0x02237],
+ "Proportional" => [0x0221D],
+ "Pscr" => [0x1D4AB],
+ "Psi" => [0x003A8],
+ "QUOT" => [0x00022],
+ "Qfr" => [0x1D514],
+ "Qopf" => [0x0211A],
+ "Qscr" => [0x1D4AC],
+ "RBarr" => [0x02910],
+ "REG" => [0x000AE],
+ "Racute" => [0x00154],
+ "Rang" => [0x027EB],
+ "Rarr" => [0x021A0],
+ "Rarrtl" => [0x02916],
+ "Rcaron" => [0x00158],
+ "Rcedil" => [0x00156],
+ "Rcy" => [0x00420],
+ "Re" => [0x0211C],
+ "ReverseElement" => [0x0220B],
+ "ReverseEquilibrium" => [0x021CB],
+ "ReverseUpEquilibrium" => [0x0296F],
+ "Rfr" => [0x0211C],
+ "Rho" => [0x003A1],
+ "RightAngleBracket" => [0x027E9],
+ "RightArrow" => [0x02192],
+ "RightArrowBar" => [0x021E5],
+ "RightArrowLeftArrow" => [0x021C4],
+ "RightCeiling" => [0x02309],
+ "RightDoubleBracket" => [0x027E7],
+ "RightDownTeeVector" => [0x0295D],
+ "RightDownVector" => [0x021C2],
+ "RightDownVectorBar" => [0x02955],
+ "RightFloor" => [0x0230B],
+ "RightTee" => [0x022A2],
+ "RightTeeArrow" => [0x021A6],
+ "RightTeeVector" => [0x0295B],
+ "RightTriangle" => [0x022B3],
+ "RightTriangleBar" => [0x029D0],
+ "RightTriangleEqual" => [0x022B5],
+ "RightUpDownVector" => [0x0294F],
+ "RightUpTeeVector" => [0x0295C],
+ "RightUpVector" => [0x021BE],
+ "RightUpVectorBar" => [0x02954],
+ "RightVector" => [0x021C0],
+ "RightVectorBar" => [0x02953],
+ "Rightarrow" => [0x021D2],
+ "Ropf" => [0x0211D],
+ "RoundImplies" => [0x02970],
+ "Rrightarrow" => [0x021DB],
+ "Rscr" => [0x0211B],
+ "Rsh" => [0x021B1],
+ "RuleDelayed" => [0x029F4],
+ "SHCHcy" => [0x00429],
+ "SHcy" => [0x00428],
+ "SOFTcy" => [0x0042C],
+ "Sacute" => [0x0015A],
+ "Sc" => [0x02ABC],
+ "Scaron" => [0x00160],
+ "Scedil" => [0x0015E],
+ "Scirc" => [0x0015C],
+ "Scy" => [0x00421],
+ "Sfr" => [0x1D516],
+ "ShortDownArrow" => [0x02193],
+ "ShortLeftArrow" => [0x02190],
+ "ShortRightArrow" => [0x02192],
+ "ShortUpArrow" => [0x02191],
+ "Sigma" => [0x003A3],
+ "SmallCircle" => [0x02218],
+ "Sopf" => [0x1D54A],
+ "Sqrt" => [0x0221A],
+ "Square" => [0x025A1],
+ "SquareIntersection" => [0x02293],
+ "SquareSubset" => [0x0228F],
+ "SquareSubsetEqual" => [0x02291],
+ "SquareSuperset" => [0x02290],
+ "SquareSupersetEqual" => [0x02292],
+ "SquareUnion" => [0x02294],
+ "Sscr" => [0x1D4AE],
+ "Star" => [0x022C6],
+ "Sub" => [0x022D0],
+ "Subset" => [0x022D0],
+ "SubsetEqual" => [0x02286],
+ "Succeeds" => [0x0227B],
+ "SucceedsEqual" => [0x02AB0],
+ "SucceedsSlantEqual" => [0x0227D],
+ "SucceedsTilde" => [0x0227F],
+ "SuchThat" => [0x0220B],
+ "Sum" => [0x02211],
+ "Sup" => [0x022D1],
+ "Superset" => [0x02283],
+ "SupersetEqual" => [0x02287],
+ "Supset" => [0x022D1],
+ "THORN" => [0x000DE],
+ "TRADE" => [0x02122],
+ "TSHcy" => [0x0040B],
+ "TScy" => [0x00426],
+ "Tab" => [0x00009],
+ "Tau" => [0x003A4],
+ "Tcaron" => [0x00164],
+ "Tcedil" => [0x00162],
+ "Tcy" => [0x00422],
+ "Tfr" => [0x1D517],
+ "Therefore" => [0x02234],
+ "Theta" => [0x00398],
+ "ThickSpace" => [0x0205F, 0x0200A],
+ "ThinSpace" => [0x02009],
+ "Tilde" => [0x0223C],
+ "TildeEqual" => [0x02243],
+ "TildeFullEqual" => [0x02245],
+ "TildeTilde" => [0x02248],
+ "Topf" => [0x1D54B],
+ "TripleDot" => [0x020DB],
+ "Tscr" => [0x1D4AF],
+ "Tstrok" => [0x00166],
+ "Uacute" => [0x000DA],
+ "Uarr" => [0x0219F],
+ "Uarrocir" => [0x02949],
+ "Ubrcy" => [0x0040E],
+ "Ubreve" => [0x0016C],
+ "Ucirc" => [0x000DB],
+ "Ucy" => [0x00423],
+ "Udblac" => [0x00170],
+ "Ufr" => [0x1D518],
+ "Ugrave" => [0x000D9],
+ "Umacr" => [0x0016A],
+ "UnderBar" => [0x0005F],
+ "UnderBrace" => [0x023DF],
+ "UnderBracket" => [0x023B5],
+ "UnderParenthesis" => [0x023DD],
+ "Union" => [0x022C3],
+ "UnionPlus" => [0x0228E],
+ "Uogon" => [0x00172],
+ "Uopf" => [0x1D54C],
+ "UpArrow" => [0x02191],
+ "UpArrowBar" => [0x02912],
+ "UpArrowDownArrow" => [0x021C5],
+ "UpDownArrow" => [0x02195],
+ "UpEquilibrium" => [0x0296E],
+ "UpTee" => [0x022A5],
+ "UpTeeArrow" => [0x021A5],
+ "Uparrow" => [0x021D1],
+ "Updownarrow" => [0x021D5],
+ "UpperLeftArrow" => [0x02196],
+ "UpperRightArrow" => [0x02197],
+ "Upsi" => [0x003D2],
+ "Upsilon" => [0x003A5],
+ "Uring" => [0x0016E],
+ "Uscr" => [0x1D4B0],
+ "Utilde" => [0x00168],
+ "Uuml" => [0x000DC],
+ "VDash" => [0x022AB],
+ "Vbar" => [0x02AEB],
+ "Vcy" => [0x00412],
+ "Vdash" => [0x022A9],
+ "Vdashl" => [0x02AE6],
+ "Vee" => [0x022C1],
+ "Verbar" => [0x02016],
+ "Vert" => [0x02016],
+ "VerticalBar" => [0x02223],
+ "VerticalLine" => [0x0007C],
+ "VerticalSeparator" => [0x02758],
+ "VerticalTilde" => [0x02240],
+ "VeryThinSpace" => [0x0200A],
+ "Vfr" => [0x1D519],
+ "Vopf" => [0x1D54D],
+ "Vscr" => [0x1D4B1],
+ "Vvdash" => [0x022AA],
+ "Wcirc" => [0x00174],
+ "Wedge" => [0x022C0],
+ "Wfr" => [0x1D51A],
+ "Wopf" => [0x1D54E],
+ "Wscr" => [0x1D4B2],
+ "Xfr" => [0x1D51B],
+ "Xi" => [0x0039E],
+ "Xopf" => [0x1D54F],
+ "Xscr" => [0x1D4B3],
+ "YAcy" => [0x0042F],
+ "YIcy" => [0x00407],
+ "YUcy" => [0x0042E],
+ "Yacute" => [0x000DD],
+ "Ycirc" => [0x00176],
+ "Ycy" => [0x0042B],
+ "Yfr" => [0x1D51C],
+ "Yopf" => [0x1D550],
+ "Yscr" => [0x1D4B4],
+ "Yuml" => [0x00178],
+ "ZHcy" => [0x00416],
+ "Zacute" => [0x00179],
+ "Zcaron" => [0x0017D],
+ "Zcy" => [0x00417],
+ "Zdot" => [0x0017B],
+ "ZeroWidthSpace" => [0x0200B],
+ "Zeta" => [0x00396],
+ "Zfr" => [0x02128],
+ "Zopf" => [0x02124],
+ "Zscr" => [0x1D4B5],
+ "aacute" => [0x000E1],
+ "abreve" => [0x00103],
+ "ac" => [0x0223E],
+ "acE" => [0x0223E, 0x00333],
+ "acd" => [0x0223F],
+ "acirc" => [0x000E2],
+ "acute" => [0x000B4],
+ "acy" => [0x00430],
+ "aelig" => [0x000E6],
+ "af" => [0x02061],
+ "afr" => [0x1D51E],
+ "agrave" => [0x000E0],
+ "alefsym" => [0x02135],
+ "aleph" => [0x02135],
+ "alpha" => [0x003B1],
+ "amacr" => [0x00101],
+ "amalg" => [0x02A3F],
+ "amp" => [0x00026],
+ "and" => [0x02227],
+ "andand" => [0x02A55],
+ "andd" => [0x02A5C],
+ "andslope" => [0x02A58],
+ "andv" => [0x02A5A],
+ "ang" => [0x02220],
+ "ange" => [0x029A4],
+ "angle" => [0x02220],
+ "angmsd" => [0x02221],
+ "angmsdaa" => [0x029A8],
+ "angmsdab" => [0x029A9],
+ "angmsdac" => [0x029AA],
+ "angmsdad" => [0x029AB],
+ "angmsdae" => [0x029AC],
+ "angmsdaf" => [0x029AD],
+ "angmsdag" => [0x029AE],
+ "angmsdah" => [0x029AF],
+ "angrt" => [0x0221F],
+ "angrtvb" => [0x022BE],
+ "angrtvbd" => [0x0299D],
+ "angsph" => [0x02222],
+ "angst" => [0x000C5],
+ "angzarr" => [0x0237C],
+ "aogon" => [0x00105],
+ "aopf" => [0x1D552],
+ "ap" => [0x02248],
+ "apE" => [0x02A70],
+ "apacir" => [0x02A6F],
+ "ape" => [0x0224A],
+ "apid" => [0x0224B],
+ "apos" => [0x00027],
+ "approx" => [0x02248],
+ "approxeq" => [0x0224A],
+ "aring" => [0x000E5],
+ "ascr" => [0x1D4B6],
+ "ast" => [0x0002A],
+ "asymp" => [0x02248],
+ "asympeq" => [0x0224D],
+ "atilde" => [0x000E3],
+ "auml" => [0x000E4],
+ "awconint" => [0x02233],
+ "awint" => [0x02A11],
+ "bNot" => [0x02AED],
+ "backcong" => [0x0224C],
+ "backepsilon" => [0x003F6],
+ "backprime" => [0x02035],
+ "backsim" => [0x0223D],
+ "backsimeq" => [0x022CD],
+ "barvee" => [0x022BD],
+ "barwed" => [0x02305],
+ "barwedge" => [0x02305],
+ "bbrk" => [0x023B5],
+ "bbrktbrk" => [0x023B6],
+ "bcong" => [0x0224C],
+ "bcy" => [0x00431],
+ "bdquo" => [0x0201E],
+ "becaus" => [0x02235],
+ "because" => [0x02235],
+ "bemptyv" => [0x029B0],
+ "bepsi" => [0x003F6],
+ "bernou" => [0x0212C],
+ "beta" => [0x003B2],
+ "beth" => [0x02136],
+ "between" => [0x0226C],
+ "bfr" => [0x1D51F],
+ "bigcap" => [0x022C2],
+ "bigcirc" => [0x025EF],
+ "bigcup" => [0x022C3],
+ "bigodot" => [0x02A00],
+ "bigoplus" => [0x02A01],
+ "bigotimes" => [0x02A02],
+ "bigsqcup" => [0x02A06],
+ "bigstar" => [0x02605],
+ "bigtriangledown" => [0x025BD],
+ "bigtriangleup" => [0x025B3],
+ "biguplus" => [0x02A04],
+ "bigvee" => [0x022C1],
+ "bigwedge" => [0x022C0],
+ "bkarow" => [0x0290D],
+ "blacklozenge" => [0x029EB],
+ "blacksquare" => [0x025AA],
+ "blacktriangle" => [0x025B4],
+ "blacktriangledown" => [0x025BE],
+ "blacktriangleleft" => [0x025C2],
+ "blacktriangleright" => [0x025B8],
+ "blank" => [0x02423],
+ "blk12" => [0x02592],
+ "blk14" => [0x02591],
+ "blk34" => [0x02593],
+ "block" => [0x02588],
+ "bne" => [0x0003D, 0x020E5],
+ "bnequiv" => [0x02261, 0x020E5],
+ "bnot" => [0x02310],
+ "bopf" => [0x1D553],
+ "bot" => [0x022A5],
+ "bottom" => [0x022A5],
+ "bowtie" => [0x022C8],
+ "boxDL" => [0x02557],
+ "boxDR" => [0x02554],
+ "boxDl" => [0x02556],
+ "boxDr" => [0x02553],
+ "boxH" => [0x02550],
+ "boxHD" => [0x02566],
+ "boxHU" => [0x02569],
+ "boxHd" => [0x02564],
+ "boxHu" => [0x02567],
+ "boxUL" => [0x0255D],
+ "boxUR" => [0x0255A],
+ "boxUl" => [0x0255C],
+ "boxUr" => [0x02559],
+ "boxV" => [0x02551],
+ "boxVH" => [0x0256C],
+ "boxVL" => [0x02563],
+ "boxVR" => [0x02560],
+ "boxVh" => [0x0256B],
+ "boxVl" => [0x02562],
+ "boxVr" => [0x0255F],
+ "boxbox" => [0x029C9],
+ "boxdL" => [0x02555],
+ "boxdR" => [0x02552],
+ "boxdl" => [0x02510],
+ "boxdr" => [0x0250C],
+ "boxh" => [0x02500],
+ "boxhD" => [0x02565],
+ "boxhU" => [0x02568],
+ "boxhd" => [0x0252C],
+ "boxhu" => [0x02534],
+ "boxminus" => [0x0229F],
+ "boxplus" => [0x0229E],
+ "boxtimes" => [0x022A0],
+ "boxuL" => [0x0255B],
+ "boxuR" => [0x02558],
+ "boxul" => [0x02518],
+ "boxur" => [0x02514],
+ "boxv" => [0x02502],
+ "boxvH" => [0x0256A],
+ "boxvL" => [0x02561],
+ "boxvR" => [0x0255E],
+ "boxvh" => [0x0253C],
+ "boxvl" => [0x02524],
+ "boxvr" => [0x0251C],
+ "bprime" => [0x02035],
+ "breve" => [0x002D8],
+ "brvbar" => [0x000A6],
+ "bscr" => [0x1D4B7],
+ "bsemi" => [0x0204F],
+ "bsim" => [0x0223D],
+ "bsime" => [0x022CD],
+ "bsol" => [0x0005C],
+ "bsolb" => [0x029C5],
+ "bsolhsub" => [0x027C8],
+ "bull" => [0x02022],
+ "bullet" => [0x02022],
+ "bump" => [0x0224E],
+ "bumpE" => [0x02AAE],
+ "bumpe" => [0x0224F],
+ "bumpeq" => [0x0224F],
+ "cacute" => [0x00107],
+ "cap" => [0x02229],
+ "capand" => [0x02A44],
+ "capbrcup" => [0x02A49],
+ "capcap" => [0x02A4B],
+ "capcup" => [0x02A47],
+ "capdot" => [0x02A40],
+ "caps" => [0x02229, 0x0FE00],
+ "caret" => [0x02041],
+ "caron" => [0x002C7],
+ "ccaps" => [0x02A4D],
+ "ccaron" => [0x0010D],
+ "ccedil" => [0x000E7],
+ "ccirc" => [0x00109],
+ "ccups" => [0x02A4C],
+ "ccupssm" => [0x02A50],
+ "cdot" => [0x0010B],
+ "cedil" => [0x000B8],
+ "cemptyv" => [0x029B2],
+ "cent" => [0x000A2],
+ "centerdot" => [0x000B7],
+ "cfr" => [0x1D520],
+ "chcy" => [0x00447],
+ "check" => [0x02713],
+ "checkmark" => [0x02713],
+ "chi" => [0x003C7],
+ "cir" => [0x025CB],
+ "cirE" => [0x029C3],
+ "circ" => [0x002C6],
+ "circeq" => [0x02257],
+ "circlearrowleft" => [0x021BA],
+ "circlearrowright" => [0x021BB],
+ "circledR" => [0x000AE],
+ "circledS" => [0x024C8],
+ "circledast" => [0x0229B],
+ "circledcirc" => [0x0229A],
+ "circleddash" => [0x0229D],
+ "cire" => [0x02257],
+ "cirfnint" => [0x02A10],
+ "cirmid" => [0x02AEF],
+ "cirscir" => [0x029C2],
+ "clubs" => [0x02663],
+ "clubsuit" => [0x02663],
+ "colon" => [0x0003A],
+ "colone" => [0x02254],
+ "coloneq" => [0x02254],
+ "comma" => [0x0002C],
+ "commat" => [0x00040],
+ "comp" => [0x02201],
+ "compfn" => [0x02218],
+ "complement" => [0x02201],
+ "complexes" => [0x02102],
+ "cong" => [0x02245],
+ "congdot" => [0x02A6D],
+ "conint" => [0x0222E],
+ "copf" => [0x1D554],
+ "coprod" => [0x02210],
+ "copy" => [0x000A9],
+ "copysr" => [0x02117],
+ "crarr" => [0x021B5],
+ "cross" => [0x02717],
+ "cscr" => [0x1D4B8],
+ "csub" => [0x02ACF],
+ "csube" => [0x02AD1],
+ "csup" => [0x02AD0],
+ "csupe" => [0x02AD2],
+ "ctdot" => [0x022EF],
+ "cudarrl" => [0x02938],
+ "cudarrr" => [0x02935],
+ "cuepr" => [0x022DE],
+ "cuesc" => [0x022DF],
+ "cularr" => [0x021B6],
+ "cularrp" => [0x0293D],
+ "cup" => [0x0222A],
+ "cupbrcap" => [0x02A48],
+ "cupcap" => [0x02A46],
+ "cupcup" => [0x02A4A],
+ "cupdot" => [0x0228D],
+ "cupor" => [0x02A45],
+ "cups" => [0x0222A, 0x0FE00],
+ "curarr" => [0x021B7],
+ "curarrm" => [0x0293C],
+ "curlyeqprec" => [0x022DE],
+ "curlyeqsucc" => [0x022DF],
+ "curlyvee" => [0x022CE],
+ "curlywedge" => [0x022CF],
+ "curren" => [0x000A4],
+ "curvearrowleft" => [0x021B6],
+ "curvearrowright" => [0x021B7],
+ "cuvee" => [0x022CE],
+ "cuwed" => [0x022CF],
+ "cwconint" => [0x02232],
+ "cwint" => [0x02231],
+ "cylcty" => [0x0232D],
+ "dArr" => [0x021D3],
+ "dHar" => [0x02965],
+ "dagger" => [0x02020],
+ "daleth" => [0x02138],
+ "darr" => [0x02193],
+ "dash" => [0x02010],
+ "dashv" => [0x022A3],
+ "dbkarow" => [0x0290F],
+ "dblac" => [0x002DD],
+ "dcaron" => [0x0010F],
+ "dcy" => [0x00434],
+ "dd" => [0x02146],
+ "ddagger" => [0x02021],
+ "ddarr" => [0x021CA],
+ "ddotseq" => [0x02A77],
+ "deg" => [0x000B0],
+ "delta" => [0x003B4],
+ "demptyv" => [0x029B1],
+ "dfisht" => [0x0297F],
+ "dfr" => [0x1D521],
+ "dharl" => [0x021C3],
+ "dharr" => [0x021C2],
+ "diam" => [0x022C4],
+ "diamond" => [0x022C4],
+ "diamondsuit" => [0x02666],
+ "diams" => [0x02666],
+ "die" => [0x000A8],
+ "digamma" => [0x003DD],
+ "disin" => [0x022F2],
+ "div" => [0x000F7],
+ "divide" => [0x000F7],
+ "divideontimes" => [0x022C7],
+ "divonx" => [0x022C7],
+ "djcy" => [0x00452],
+ "dlcorn" => [0x0231E],
+ "dlcrop" => [0x0230D],
+ "dollar" => [0x00024],
+ "dopf" => [0x1D555],
+ "dot" => [0x002D9],
+ "doteq" => [0x02250],
+ "doteqdot" => [0x02251],
+ "dotminus" => [0x02238],
+ "dotplus" => [0x02214],
+ "dotsquare" => [0x022A1],
+ "doublebarwedge" => [0x02306],
+ "downarrow" => [0x02193],
+ "downdownarrows" => [0x021CA],
+ "downharpoonleft" => [0x021C3],
+ "downharpoonright" => [0x021C2],
+ "drbkarow" => [0x02910],
+ "drcorn" => [0x0231F],
+ "drcrop" => [0x0230C],
+ "dscr" => [0x1D4B9],
+ "dscy" => [0x00455],
+ "dsol" => [0x029F6],
+ "dstrok" => [0x00111],
+ "dtdot" => [0x022F1],
+ "dtri" => [0x025BF],
+ "dtrif" => [0x025BE],
+ "duarr" => [0x021F5],
+ "duhar" => [0x0296F],
+ "dwangle" => [0x029A6],
+ "dzcy" => [0x0045F],
+ "dzigrarr" => [0x027FF],
+ "eDDot" => [0x02A77],
+ "eDot" => [0x02251],
+ "eacute" => [0x000E9],
+ "easter" => [0x02A6E],
+ "ecaron" => [0x0011B],
+ "ecir" => [0x02256],
+ "ecirc" => [0x000EA],
+ "ecolon" => [0x02255],
+ "ecy" => [0x0044D],
+ "edot" => [0x00117],
+ "ee" => [0x02147],
+ "efDot" => [0x02252],
+ "efr" => [0x1D522],
+ "eg" => [0x02A9A],
+ "egrave" => [0x000E8],
+ "egs" => [0x02A96],
+ "egsdot" => [0x02A98],
+ "el" => [0x02A99],
+ "elinters" => [0x023E7],
+ "ell" => [0x02113],
+ "els" => [0x02A95],
+ "elsdot" => [0x02A97],
+ "emacr" => [0x00113],
+ "empty" => [0x02205],
+ "emptyset" => [0x02205],
+ "emptyv" => [0x02205],
+ "emsp" => [0x02003],
+ "emsp13" => [0x02004],
+ "emsp14" => [0x02005],
+ "eng" => [0x0014B],
+ "ensp" => [0x02002],
+ "eogon" => [0x00119],
+ "eopf" => [0x1D556],
+ "epar" => [0x022D5],
+ "eparsl" => [0x029E3],
+ "eplus" => [0x02A71],
+ "epsi" => [0x003B5],
+ "epsilon" => [0x003B5],
+ "epsiv" => [0x003F5],
+ "eqcirc" => [0x02256],
+ "eqcolon" => [0x02255],
+ "eqsim" => [0x02242],
+ "eqslantgtr" => [0x02A96],
+ "eqslantless" => [0x02A95],
+ "equals" => [0x0003D],
+ "equest" => [0x0225F],
+ "equiv" => [0x02261],
+ "equivDD" => [0x02A78],
+ "eqvparsl" => [0x029E5],
+ "erDot" => [0x02253],
+ "erarr" => [0x02971],
+ "escr" => [0x0212F],
+ "esdot" => [0x02250],
+ "esim" => [0x02242],
+ "eta" => [0x003B7],
+ "eth" => [0x000F0],
+ "euml" => [0x000EB],
+ "euro" => [0x020AC],
+ "excl" => [0x00021],
+ "exist" => [0x02203],
+ "expectation" => [0x02130],
+ "exponentiale" => [0x02147],
+ "fallingdotseq" => [0x02252],
+ "fcy" => [0x00444],
+ "female" => [0x02640],
+ "ffilig" => [0x0FB03],
+ "fflig" => [0x0FB00],
+ "ffllig" => [0x0FB04],
+ "ffr" => [0x1D523],
+ "filig" => [0x0FB01],
+ "fjlig" => [0x00066, 0x0006A],
+ "flat" => [0x0266D],
+ "fllig" => [0x0FB02],
+ "fltns" => [0x025B1],
+ "fnof" => [0x00192],
+ "fopf" => [0x1D557],
+ "forall" => [0x02200],
+ "fork" => [0x022D4],
+ "forkv" => [0x02AD9],
+ "fpartint" => [0x02A0D],
+ "frac12" => [0x000BD],
+ "frac13" => [0x02153],
+ "frac14" => [0x000BC],
+ "frac15" => [0x02155],
+ "frac16" => [0x02159],
+ "frac18" => [0x0215B],
+ "frac23" => [0x02154],
+ "frac25" => [0x02156],
+ "frac34" => [0x000BE],
+ "frac35" => [0x02157],
+ "frac38" => [0x0215C],
+ "frac45" => [0x02158],
+ "frac56" => [0x0215A],
+ "frac58" => [0x0215D],
+ "frac78" => [0x0215E],
+ "frasl" => [0x02044],
+ "frown" => [0x02322],
+ "fscr" => [0x1D4BB],
+ "gE" => [0x02267],
+ "gEl" => [0x02A8C],
+ "gacute" => [0x001F5],
+ "gamma" => [0x003B3],
+ "gammad" => [0x003DD],
+ "gap" => [0x02A86],
+ "gbreve" => [0x0011F],
+ "gcirc" => [0x0011D],
+ "gcy" => [0x00433],
+ "gdot" => [0x00121],
+ "ge" => [0x02265],
+ "gel" => [0x022DB],
+ "geq" => [0x02265],
+ "geqq" => [0x02267],
+ "geqslant" => [0x02A7E],
+ "ges" => [0x02A7E],
+ "gescc" => [0x02AA9],
+ "gesdot" => [0x02A80],
+ "gesdoto" => [0x02A82],
+ "gesdotol" => [0x02A84],
+ "gesl" => [0x022DB, 0x0FE00],
+ "gesles" => [0x02A94],
+ "gfr" => [0x1D524],
+ "gg" => [0x0226B],
+ "ggg" => [0x022D9],
+ "gimel" => [0x02137],
+ "gjcy" => [0x00453],
+ "gl" => [0x02277],
+ "glE" => [0x02A92],
+ "gla" => [0x02AA5],
+ "glj" => [0x02AA4],
+ "gnE" => [0x02269],
+ "gnap" => [0x02A8A],
+ "gnapprox" => [0x02A8A],
+ "gne" => [0x02A88],
+ "gneq" => [0x02A88],
+ "gneqq" => [0x02269],
+ "gnsim" => [0x022E7],
+ "gopf" => [0x1D558],
+ "grave" => [0x00060],
+ "gscr" => [0x0210A],
+ "gsim" => [0x02273],
+ "gsime" => [0x02A8E],
+ "gsiml" => [0x02A90],
+ "gt" => [0x0003E],
+ "gtcc" => [0x02AA7],
+ "gtcir" => [0x02A7A],
+ "gtdot" => [0x022D7],
+ "gtlPar" => [0x02995],
+ "gtquest" => [0x02A7C],
+ "gtrapprox" => [0x02A86],
+ "gtrarr" => [0x02978],
+ "gtrdot" => [0x022D7],
+ "gtreqless" => [0x022DB],
+ "gtreqqless" => [0x02A8C],
+ "gtrless" => [0x02277],
+ "gtrsim" => [0x02273],
+ "gvertneqq" => [0x02269, 0x0FE00],
+ "gvnE" => [0x02269, 0x0FE00],
+ "hArr" => [0x021D4],
+ "hairsp" => [0x0200A],
+ "half" => [0x000BD],
+ "hamilt" => [0x0210B],
+ "hardcy" => [0x0044A],
+ "harr" => [0x02194],
+ "harrcir" => [0x02948],
+ "harrw" => [0x021AD],
+ "hbar" => [0x0210F],
+ "hcirc" => [0x00125],
+ "hearts" => [0x02665],
+ "heartsuit" => [0x02665],
+ "hellip" => [0x02026],
+ "hercon" => [0x022B9],
+ "hfr" => [0x1D525],
+ "hksearow" => [0x02925],
+ "hkswarow" => [0x02926],
+ "hoarr" => [0x021FF],
+ "homtht" => [0x0223B],
+ "hookleftarrow" => [0x021A9],
+ "hookrightarrow" => [0x021AA],
+ "hopf" => [0x1D559],
+ "horbar" => [0x02015],
+ "hscr" => [0x1D4BD],
+ "hslash" => [0x0210F],
+ "hstrok" => [0x00127],
+ "hybull" => [0x02043],
+ "hyphen" => [0x02010],
+ "iacute" => [0x000ED],
+ "ic" => [0x02063],
+ "icirc" => [0x000EE],
+ "icy" => [0x00438],
+ "iecy" => [0x00435],
+ "iexcl" => [0x000A1],
+ "iff" => [0x021D4],
+ "ifr" => [0x1D526],
+ "igrave" => [0x000EC],
+ "ii" => [0x02148],
+ "iiiint" => [0x02A0C],
+ "iiint" => [0x0222D],
+ "iinfin" => [0x029DC],
+ "iiota" => [0x02129],
+ "ijlig" => [0x00133],
+ "imacr" => [0x0012B],
+ "image" => [0x02111],
+ "imagline" => [0x02110],
+ "imagpart" => [0x02111],
+ "imath" => [0x00131],
+ "imof" => [0x022B7],
+ "imped" => [0x001B5],
+ "in" => [0x02208],
+ "incare" => [0x02105],
+ "infin" => [0x0221E],
+ "infintie" => [0x029DD],
+ "inodot" => [0x00131],
+ "int" => [0x0222B],
+ "intcal" => [0x022BA],
+ "integers" => [0x02124],
+ "intercal" => [0x022BA],
+ "intlarhk" => [0x02A17],
+ "intprod" => [0x02A3C],
+ "iocy" => [0x00451],
+ "iogon" => [0x0012F],
+ "iopf" => [0x1D55A],
+ "iota" => [0x003B9],
+ "iprod" => [0x02A3C],
+ "iquest" => [0x000BF],
+ "iscr" => [0x1D4BE],
+ "isin" => [0x02208],
+ "isinE" => [0x022F9],
+ "isindot" => [0x022F5],
+ "isins" => [0x022F4],
+ "isinsv" => [0x022F3],
+ "isinv" => [0x02208],
+ "it" => [0x02062],
+ "itilde" => [0x00129],
+ "iukcy" => [0x00456],
+ "iuml" => [0x000EF],
+ "jcirc" => [0x00135],
+ "jcy" => [0x00439],
+ "jfr" => [0x1D527],
+ "jmath" => [0x00237],
+ "jopf" => [0x1D55B],
+ "jscr" => [0x1D4BF],
+ "jsercy" => [0x00458],
+ "jukcy" => [0x00454],
+ "kappa" => [0x003BA],
+ "kappav" => [0x003F0],
+ "kcedil" => [0x00137],
+ "kcy" => [0x0043A],
+ "kfr" => [0x1D528],
+ "kgreen" => [0x00138],
+ "khcy" => [0x00445],
+ "kjcy" => [0x0045C],
+ "kopf" => [0x1D55C],
+ "kscr" => [0x1D4C0],
+ "lAarr" => [0x021DA],
+ "lArr" => [0x021D0],
+ "lAtail" => [0x0291B],
+ "lBarr" => [0x0290E],
+ "lE" => [0x02266],
+ "lEg" => [0x02A8B],
+ "lHar" => [0x02962],
+ "lacute" => [0x0013A],
+ "laemptyv" => [0x029B4],
+ "lagran" => [0x02112],
+ "lambda" => [0x003BB],
+ "lang" => [0x027E8],
+ "langd" => [0x02991],
+ "langle" => [0x027E8],
+ "lap" => [0x02A85],
+ "laquo" => [0x000AB],
+ "larr" => [0x02190],
+ "larrb" => [0x021E4],
+ "larrbfs" => [0x0291F],
+ "larrfs" => [0x0291D],
+ "larrhk" => [0x021A9],
+ "larrlp" => [0x021AB],
+ "larrpl" => [0x02939],
+ "larrsim" => [0x02973],
+ "larrtl" => [0x021A2],
+ "lat" => [0x02AAB],
+ "latail" => [0x02919],
+ "late" => [0x02AAD],
+ "lates" => [0x02AAD, 0x0FE00],
+ "lbarr" => [0x0290C],
+ "lbbrk" => [0x02772],
+ "lbrace" => [0x0007B],
+ "lbrack" => [0x0005B],
+ "lbrke" => [0x0298B],
+ "lbrksld" => [0x0298F],
+ "lbrkslu" => [0x0298D],
+ "lcaron" => [0x0013E],
+ "lcedil" => [0x0013C],
+ "lceil" => [0x02308],
+ "lcub" => [0x0007B],
+ "lcy" => [0x0043B],
+ "ldca" => [0x02936],
+ "ldquo" => [0x0201C],
+ "ldquor" => [0x0201E],
+ "ldrdhar" => [0x02967],
+ "ldrushar" => [0x0294B],
+ "ldsh" => [0x021B2],
+ "le" => [0x02264],
+ "leftarrow" => [0x02190],
+ "leftarrowtail" => [0x021A2],
+ "leftharpoondown" => [0x021BD],
+ "leftharpoonup" => [0x021BC],
+ "leftleftarrows" => [0x021C7],
+ "leftrightarrow" => [0x02194],
+ "leftrightarrows" => [0x021C6],
+ "leftrightharpoons" => [0x021CB],
+ "leftrightsquigarrow" => [0x021AD],
+ "leftthreetimes" => [0x022CB],
+ "leg" => [0x022DA],
+ "leq" => [0x02264],
+ "leqq" => [0x02266],
+ "leqslant" => [0x02A7D],
+ "les" => [0x02A7D],
+ "lescc" => [0x02AA8],
+ "lesdot" => [0x02A7F],
+ "lesdoto" => [0x02A81],
+ "lesdotor" => [0x02A83],
+ "lesg" => [0x022DA, 0x0FE00],
+ "lesges" => [0x02A93],
+ "lessapprox" => [0x02A85],
+ "lessdot" => [0x022D6],
+ "lesseqgtr" => [0x022DA],
+ "lesseqqgtr" => [0x02A8B],
+ "lessgtr" => [0x02276],
+ "lesssim" => [0x02272],
+ "lfisht" => [0x0297C],
+ "lfloor" => [0x0230A],
+ "lfr" => [0x1D529],
+ "lg" => [0x02276],
+ "lgE" => [0x02A91],
+ "lhard" => [0x021BD],
+ "lharu" => [0x021BC],
+ "lharul" => [0x0296A],
+ "lhblk" => [0x02584],
+ "ljcy" => [0x00459],
+ "ll" => [0x0226A],
+ "llarr" => [0x021C7],
+ "llcorner" => [0x0231E],
+ "llhard" => [0x0296B],
+ "lltri" => [0x025FA],
+ "lmidot" => [0x00140],
+ "lmoust" => [0x023B0],
+ "lmoustache" => [0x023B0],
+ "lnE" => [0x02268],
+ "lnap" => [0x02A89],
+ "lnapprox" => [0x02A89],
+ "lne" => [0x02A87],
+ "lneq" => [0x02A87],
+ "lneqq" => [0x02268],
+ "lnsim" => [0x022E6],
+ "loang" => [0x027EC],
+ "loarr" => [0x021FD],
+ "lobrk" => [0x027E6],
+ "longleftarrow" => [0x027F5],
+ "longleftrightarrow" => [0x027F7],
+ "longmapsto" => [0x027FC],
+ "longrightarrow" => [0x027F6],
+ "looparrowleft" => [0x021AB],
+ "looparrowright" => [0x021AC],
+ "lopar" => [0x02985],
+ "lopf" => [0x1D55D],
+ "loplus" => [0x02A2D],
+ "lotimes" => [0x02A34],
+ "lowast" => [0x02217],
+ "lowbar" => [0x0005F],
+ "loz" => [0x025CA],
+ "lozenge" => [0x025CA],
+ "lozf" => [0x029EB],
+ "lpar" => [0x00028],
+ "lparlt" => [0x02993],
+ "lrarr" => [0x021C6],
+ "lrcorner" => [0x0231F],
+ "lrhar" => [0x021CB],
+ "lrhard" => [0x0296D],
+ "lrm" => [0x0200E],
+ "lrtri" => [0x022BF],
+ "lsaquo" => [0x02039],
+ "lscr" => [0x1D4C1],
+ "lsh" => [0x021B0],
+ "lsim" => [0x02272],
+ "lsime" => [0x02A8D],
+ "lsimg" => [0x02A8F],
+ "lsqb" => [0x0005B],
+ "lsquo" => [0x02018],
+ "lsquor" => [0x0201A],
+ "lstrok" => [0x00142],
+ "lt" => [0x0003C],
+ "ltcc" => [0x02AA6],
+ "ltcir" => [0x02A79],
+ "ltdot" => [0x022D6],
+ "lthree" => [0x022CB],
+ "ltimes" => [0x022C9],
+ "ltlarr" => [0x02976],
+ "ltquest" => [0x02A7B],
+ "ltrPar" => [0x02996],
+ "ltri" => [0x025C3],
+ "ltrie" => [0x022B4],
+ "ltrif" => [0x025C2],
+ "lurdshar" => [0x0294A],
+ "luruhar" => [0x02966],
+ "lvertneqq" => [0x02268, 0x0FE00],
+ "lvnE" => [0x02268, 0x0FE00],
+ "mDDot" => [0x0223A],
+ "macr" => [0x000AF],
+ "male" => [0x02642],
+ "malt" => [0x02720],
+ "maltese" => [0x02720],
+ "map" => [0x021A6],
+ "mapsto" => [0x021A6],
+ "mapstodown" => [0x021A7],
+ "mapstoleft" => [0x021A4],
+ "mapstoup" => [0x021A5],
+ "marker" => [0x025AE],
+ "mcomma" => [0x02A29],
+ "mcy" => [0x0043C],
+ "mdash" => [0x02014],
+ "measuredangle" => [0x02221],
+ "mfr" => [0x1D52A],
+ "mho" => [0x02127],
+ "micro" => [0x000B5],
+ "mid" => [0x02223],
+ "midast" => [0x0002A],
+ "midcir" => [0x02AF0],
+ "middot" => [0x000B7],
+ "minus" => [0x02212],
+ "minusb" => [0x0229F],
+ "minusd" => [0x02238],
+ "minusdu" => [0x02A2A],
+ "mlcp" => [0x02ADB],
+ "mldr" => [0x02026],
+ "mnplus" => [0x02213],
+ "models" => [0x022A7],
+ "mopf" => [0x1D55E],
+ "mp" => [0x02213],
+ "mscr" => [0x1D4C2],
+ "mstpos" => [0x0223E],
+ "mu" => [0x003BC],
+ "multimap" => [0x022B8],
+ "mumap" => [0x022B8],
+ "nGg" => [0x022D9, 0x00338],
+ "nGt" => [0x0226B, 0x020D2],
+ "nGtv" => [0x0226B, 0x00338],
+ "nLeftarrow" => [0x021CD],
+ "nLeftrightarrow" => [0x021CE],
+ "nLl" => [0x022D8, 0x00338],
+ "nLt" => [0x0226A, 0x020D2],
+ "nLtv" => [0x0226A, 0x00338],
+ "nRightarrow" => [0x021CF],
+ "nVDash" => [0x022AF],
+ "nVdash" => [0x022AE],
+ "nabla" => [0x02207],
+ "nacute" => [0x00144],
+ "nang" => [0x02220, 0x020D2],
+ "nap" => [0x02249],
+ "napE" => [0x02A70, 0x00338],
+ "napid" => [0x0224B, 0x00338],
+ "napos" => [0x00149],
+ "napprox" => [0x02249],
+ "natur" => [0x0266E],
+ "natural" => [0x0266E],
+ "naturals" => [0x02115],
+ "nbsp" => [0x000A0],
+ "nbump" => [0x0224E, 0x00338],
+ "nbumpe" => [0x0224F, 0x00338],
+ "ncap" => [0x02A43],
+ "ncaron" => [0x00148],
+ "ncedil" => [0x00146],
+ "ncong" => [0x02247],
+ "ncongdot" => [0x02A6D, 0x00338],
+ "ncup" => [0x02A42],
+ "ncy" => [0x0043D],
+ "ndash" => [0x02013],
+ "ne" => [0x02260],
+ "neArr" => [0x021D7],
+ "nearhk" => [0x02924],
+ "nearr" => [0x02197],
+ "nearrow" => [0x02197],
+ "nedot" => [0x02250, 0x00338],
+ "nequiv" => [0x02262],
+ "nesear" => [0x02928],
+ "nesim" => [0x02242, 0x00338],
+ "nexist" => [0x02204],
+ "nexists" => [0x02204],
+ "nfr" => [0x1D52B],
+ "ngE" => [0x02267, 0x00338],
+ "nge" => [0x02271],
+ "ngeq" => [0x02271],
+ "ngeqq" => [0x02267, 0x00338],
+ "ngeqslant" => [0x02A7E, 0x00338],
+ "nges" => [0x02A7E, 0x00338],
+ "ngsim" => [0x02275],
+ "ngt" => [0x0226F],
+ "ngtr" => [0x0226F],
+ "nhArr" => [0x021CE],
+ "nharr" => [0x021AE],
+ "nhpar" => [0x02AF2],
+ "ni" => [0x0220B],
+ "nis" => [0x022FC],
+ "nisd" => [0x022FA],
+ "niv" => [0x0220B],
+ "njcy" => [0x0045A],
+ "nlArr" => [0x021CD],
+ "nlE" => [0x02266, 0x00338],
+ "nlarr" => [0x0219A],
+ "nldr" => [0x02025],
+ "nle" => [0x02270],
+ "nleftarrow" => [0x0219A],
+ "nleftrightarrow" => [0x021AE],
+ "nleq" => [0x02270],
+ "nleqq" => [0x02266, 0x00338],
+ "nleqslant" => [0x02A7D, 0x00338],
+ "nles" => [0x02A7D, 0x00338],
+ "nless" => [0x0226E],
+ "nlsim" => [0x02274],
+ "nlt" => [0x0226E],
+ "nltri" => [0x022EA],
+ "nltrie" => [0x022EC],
+ "nmid" => [0x02224],
+ "nopf" => [0x1D55F],
+ "not" => [0x000AC],
+ "notin" => [0x02209],
+ "notinE" => [0x022F9, 0x00338],
+ "notindot" => [0x022F5, 0x00338],
+ "notinva" => [0x02209],
+ "notinvb" => [0x022F7],
+ "notinvc" => [0x022F6],
+ "notni" => [0x0220C],
+ "notniva" => [0x0220C],
+ "notnivb" => [0x022FE],
+ "notnivc" => [0x022FD],
+ "npar" => [0x02226],
+ "nparallel" => [0x02226],
+ "nparsl" => [0x02AFD, 0x020E5],
+ "npart" => [0x02202, 0x00338],
+ "npolint" => [0x02A14],
+ "npr" => [0x02280],
+ "nprcue" => [0x022E0],
+ "npre" => [0x02AAF, 0x00338],
+ "nprec" => [0x02280],
+ "npreceq" => [0x02AAF, 0x00338],
+ "nrArr" => [0x021CF],
+ "nrarr" => [0x0219B],
+ "nrarrc" => [0x02933, 0x00338],
+ "nrarrw" => [0x0219D, 0x00338],
+ "nrightarrow" => [0x0219B],
+ "nrtri" => [0x022EB],
+ "nrtrie" => [0x022ED],
+ "nsc" => [0x02281],
+ "nsccue" => [0x022E1],
+ "nsce" => [0x02AB0, 0x00338],
+ "nscr" => [0x1D4C3],
+ "nshortmid" => [0x02224],
+ "nshortparallel" => [0x02226],
+ "nsim" => [0x02241],
+ "nsime" => [0x02244],
+ "nsimeq" => [0x02244],
+ "nsmid" => [0x02224],
+ "nspar" => [0x02226],
+ "nsqsube" => [0x022E2],
+ "nsqsupe" => [0x022E3],
+ "nsub" => [0x02284],
+ "nsubE" => [0x02AC5, 0x00338],
+ "nsube" => [0x02288],
+ "nsubset" => [0x02282, 0x020D2],
+ "nsubseteq" => [0x02288],
+ "nsubseteqq" => [0x02AC5, 0x00338],
+ "nsucc" => [0x02281],
+ "nsucceq" => [0x02AB0, 0x00338],
+ "nsup" => [0x02285],
+ "nsupE" => [0x02AC6, 0x00338],
+ "nsupe" => [0x02289],
+ "nsupset" => [0x02283, 0x020D2],
+ "nsupseteq" => [0x02289],
+ "nsupseteqq" => [0x02AC6, 0x00338],
+ "ntgl" => [0x02279],
+ "ntilde" => [0x000F1],
+ "ntlg" => [0x02278],
+ "ntriangleleft" => [0x022EA],
+ "ntrianglelefteq" => [0x022EC],
+ "ntriangleright" => [0x022EB],
+ "ntrianglerighteq" => [0x022ED],
+ "nu" => [0x003BD],
+ "num" => [0x00023],
+ "numero" => [0x02116],
+ "numsp" => [0x02007],
+ "nvDash" => [0x022AD],
+ "nvHarr" => [0x02904],
+ "nvap" => [0x0224D, 0x020D2],
+ "nvdash" => [0x022AC],
+ "nvge" => [0x02265, 0x020D2],
+ "nvgt" => [0x0003E, 0x020D2],
+ "nvinfin" => [0x029DE],
+ "nvlArr" => [0x02902],
+ "nvle" => [0x02264, 0x020D2],
+ "nvlt" => [0x0003C, 0x020D2],
+ "nvltrie" => [0x022B4, 0x020D2],
+ "nvrArr" => [0x02903],
+ "nvrtrie" => [0x022B5, 0x020D2],
+ "nvsim" => [0x0223C, 0x020D2],
+ "nwArr" => [0x021D6],
+ "nwarhk" => [0x02923],
+ "nwarr" => [0x02196],
+ "nwarrow" => [0x02196],
+ "nwnear" => [0x02927],
+ "oS" => [0x024C8],
+ "oacute" => [0x000F3],
+ "oast" => [0x0229B],
+ "ocir" => [0x0229A],
+ "ocirc" => [0x000F4],
+ "ocy" => [0x0043E],
+ "odash" => [0x0229D],
+ "odblac" => [0x00151],
+ "odiv" => [0x02A38],
+ "odot" => [0x02299],
+ "odsold" => [0x029BC],
+ "oelig" => [0x00153],
+ "ofcir" => [0x029BF],
+ "ofr" => [0x1D52C],
+ "ogon" => [0x002DB],
+ "ograve" => [0x000F2],
+ "ogt" => [0x029C1],
+ "ohbar" => [0x029B5],
+ "ohm" => [0x003A9],
+ "oint" => [0x0222E],
+ "olarr" => [0x021BA],
+ "olcir" => [0x029BE],
+ "olcross" => [0x029BB],
+ "oline" => [0x0203E],
+ "olt" => [0x029C0],
+ "omacr" => [0x0014D],
+ "omega" => [0x003C9],
+ "omicron" => [0x003BF],
+ "omid" => [0x029B6],
+ "ominus" => [0x02296],
+ "oopf" => [0x1D560],
+ "opar" => [0x029B7],
+ "operp" => [0x029B9],
+ "oplus" => [0x02295],
+ "or" => [0x02228],
+ "orarr" => [0x021BB],
+ "ord" => [0x02A5D],
+ "order" => [0x02134],
+ "orderof" => [0x02134],
+ "ordf" => [0x000AA],
+ "ordm" => [0x000BA],
+ "origof" => [0x022B6],
+ "oror" => [0x02A56],
+ "orslope" => [0x02A57],
+ "orv" => [0x02A5B],
+ "oscr" => [0x02134],
+ "oslash" => [0x000F8],
+ "osol" => [0x02298],
+ "otilde" => [0x000F5],
+ "otimes" => [0x02297],
+ "otimesas" => [0x02A36],
+ "ouml" => [0x000F6],
+ "ovbar" => [0x0233D],
+ "par" => [0x02225],
+ "para" => [0x000B6],
+ "parallel" => [0x02225],
+ "parsim" => [0x02AF3],
+ "parsl" => [0x02AFD],
+ "part" => [0x02202],
+ "pcy" => [0x0043F],
+ "percnt" => [0x00025],
+ "period" => [0x0002E],
+ "permil" => [0x02030],
+ "perp" => [0x022A5],
+ "pertenk" => [0x02031],
+ "pfr" => [0x1D52D],
+ "phi" => [0x003C6],
+ "phiv" => [0x003D5],
+ "phmmat" => [0x02133],
+ "phone" => [0x0260E],
+ "pi" => [0x003C0],
+ "pitchfork" => [0x022D4],
+ "piv" => [0x003D6],
+ "planck" => [0x0210F],
+ "planckh" => [0x0210E],
+ "plankv" => [0x0210F],
+ "plus" => [0x0002B],
+ "plusacir" => [0x02A23],
+ "plusb" => [0x0229E],
+ "pluscir" => [0x02A22],
+ "plusdo" => [0x02214],
+ "plusdu" => [0x02A25],
+ "pluse" => [0x02A72],
+ "plusmn" => [0x000B1],
+ "plussim" => [0x02A26],
+ "plustwo" => [0x02A27],
+ "pm" => [0x000B1],
+ "pointint" => [0x02A15],
+ "popf" => [0x1D561],
+ "pound" => [0x000A3],
+ "pr" => [0x0227A],
+ "prE" => [0x02AB3],
+ "prap" => [0x02AB7],
+ "prcue" => [0x0227C],
+ "pre" => [0x02AAF],
+ "prec" => [0x0227A],
+ "precapprox" => [0x02AB7],
+ "preccurlyeq" => [0x0227C],
+ "preceq" => [0x02AAF],
+ "precnapprox" => [0x02AB9],
+ "precneqq" => [0x02AB5],
+ "precnsim" => [0x022E8],
+ "precsim" => [0x0227E],
+ "prime" => [0x02032],
+ "primes" => [0x02119],
+ "prnE" => [0x02AB5],
+ "prnap" => [0x02AB9],
+ "prnsim" => [0x022E8],
+ "prod" => [0x0220F],
+ "profalar" => [0x0232E],
+ "profline" => [0x02312],
+ "profsurf" => [0x02313],
+ "prop" => [0x0221D],
+ "propto" => [0x0221D],
+ "prsim" => [0x0227E],
+ "prurel" => [0x022B0],
+ "pscr" => [0x1D4C5],
+ "psi" => [0x003C8],
+ "puncsp" => [0x02008],
+ "qfr" => [0x1D52E],
+ "qint" => [0x02A0C],
+ "qopf" => [0x1D562],
+ "qprime" => [0x02057],
+ "qscr" => [0x1D4C6],
+ "quaternions" => [0x0210D],
+ "quatint" => [0x02A16],
+ "quest" => [0x0003F],
+ "questeq" => [0x0225F],
+ "quot" => [0x00022],
+ "rAarr" => [0x021DB],
+ "rArr" => [0x021D2],
+ "rAtail" => [0x0291C],
+ "rBarr" => [0x0290F],
+ "rHar" => [0x02964],
+ "race" => [0x0223D, 0x00331],
+ "racute" => [0x00155],
+ "radic" => [0x0221A],
+ "raemptyv" => [0x029B3],
+ "rang" => [0x027E9],
+ "rangd" => [0x02992],
+ "range" => [0x029A5],
+ "rangle" => [0x027E9],
+ "raquo" => [0x000BB],
+ "rarr" => [0x02192],
+ "rarrap" => [0x02975],
+ "rarrb" => [0x021E5],
+ "rarrbfs" => [0x02920],
+ "rarrc" => [0x02933],
+ "rarrfs" => [0x0291E],
+ "rarrhk" => [0x021AA],
+ "rarrlp" => [0x021AC],
+ "rarrpl" => [0x02945],
+ "rarrsim" => [0x02974],
+ "rarrtl" => [0x021A3],
+ "rarrw" => [0x0219D],
+ "ratail" => [0x0291A],
+ "ratio" => [0x02236],
+ "rationals" => [0x0211A],
+ "rbarr" => [0x0290D],
+ "rbbrk" => [0x02773],
+ "rbrace" => [0x0007D],
+ "rbrack" => [0x0005D],
+ "rbrke" => [0x0298C],
+ "rbrksld" => [0x0298E],
+ "rbrkslu" => [0x02990],
+ "rcaron" => [0x00159],
+ "rcedil" => [0x00157],
+ "rceil" => [0x02309],
+ "rcub" => [0x0007D],
+ "rcy" => [0x00440],
+ "rdca" => [0x02937],
+ "rdldhar" => [0x02969],
+ "rdquo" => [0x0201D],
+ "rdquor" => [0x0201D],
+ "rdsh" => [0x021B3],
+ "real" => [0x0211C],
+ "realine" => [0x0211B],
+ "realpart" => [0x0211C],
+ "reals" => [0x0211D],
+ "rect" => [0x025AD],
+ "reg" => [0x000AE],
+ "rfisht" => [0x0297D],
+ "rfloor" => [0x0230B],
+ "rfr" => [0x1D52F],
+ "rhard" => [0x021C1],
+ "rharu" => [0x021C0],
+ "rharul" => [0x0296C],
+ "rho" => [0x003C1],
+ "rhov" => [0x003F1],
+ "rightarrow" => [0x02192],
+ "rightarrowtail" => [0x021A3],
+ "rightharpoondown" => [0x021C1],
+ "rightharpoonup" => [0x021C0],
+ "rightleftarrows" => [0x021C4],
+ "rightleftharpoons" => [0x021CC],
+ "rightrightarrows" => [0x021C9],
+ "rightsquigarrow" => [0x0219D],
+ "rightthreetimes" => [0x022CC],
+ "ring" => [0x002DA],
+ "risingdotseq" => [0x02253],
+ "rlarr" => [0x021C4],
+ "rlhar" => [0x021CC],
+ "rlm" => [0x0200F],
+ "rmoust" => [0x023B1],
+ "rmoustache" => [0x023B1],
+ "rnmid" => [0x02AEE],
+ "roang" => [0x027ED],
+ "roarr" => [0x021FE],
+ "robrk" => [0x027E7],
+ "ropar" => [0x02986],
+ "ropf" => [0x1D563],
+ "roplus" => [0x02A2E],
+ "rotimes" => [0x02A35],
+ "rpar" => [0x00029],
+ "rpargt" => [0x02994],
+ "rppolint" => [0x02A12],
+ "rrarr" => [0x021C9],
+ "rsaquo" => [0x0203A],
+ "rscr" => [0x1D4C7],
+ "rsh" => [0x021B1],
+ "rsqb" => [0x0005D],
+ "rsquo" => [0x02019],
+ "rsquor" => [0x02019],
+ "rthree" => [0x022CC],
+ "rtimes" => [0x022CA],
+ "rtri" => [0x025B9],
+ "rtrie" => [0x022B5],
+ "rtrif" => [0x025B8],
+ "rtriltri" => [0x029CE],
+ "ruluhar" => [0x02968],
+ "rx" => [0x0211E],
+ "sacute" => [0x0015B],
+ "sbquo" => [0x0201A],
+ "sc" => [0x0227B],
+ "scE" => [0x02AB4],
+ "scap" => [0x02AB8],
+ "scaron" => [0x00161],
+ "sccue" => [0x0227D],
+ "sce" => [0x02AB0],
+ "scedil" => [0x0015F],
+ "scirc" => [0x0015D],
+ "scnE" => [0x02AB6],
+ "scnap" => [0x02ABA],
+ "scnsim" => [0x022E9],
+ "scpolint" => [0x02A13],
+ "scsim" => [0x0227F],
+ "scy" => [0x00441],
+ "sdot" => [0x022C5],
+ "sdotb" => [0x022A1],
+ "sdote" => [0x02A66],
+ "seArr" => [0x021D8],
+ "searhk" => [0x02925],
+ "searr" => [0x02198],
+ "searrow" => [0x02198],
+ "sect" => [0x000A7],
+ "semi" => [0x0003B],
+ "seswar" => [0x02929],
+ "setminus" => [0x02216],
+ "setmn" => [0x02216],
+ "sext" => [0x02736],
+ "sfr" => [0x1D530],
+ "sfrown" => [0x02322],
+ "sharp" => [0x0266F],
+ "shchcy" => [0x00449],
+ "shcy" => [0x00448],
+ "shortmid" => [0x02223],
+ "shortparallel" => [0x02225],
+ "shy" => [0x000AD],
+ "sigma" => [0x003C3],
+ "sigmaf" => [0x003C2],
+ "sigmav" => [0x003C2],
+ "sim" => [0x0223C],
+ "simdot" => [0x02A6A],
+ "sime" => [0x02243],
+ "simeq" => [0x02243],
+ "simg" => [0x02A9E],
+ "simgE" => [0x02AA0],
+ "siml" => [0x02A9D],
+ "simlE" => [0x02A9F],
+ "simne" => [0x02246],
+ "simplus" => [0x02A24],
+ "simrarr" => [0x02972],
+ "slarr" => [0x02190],
+ "smallsetminus" => [0x02216],
+ "smashp" => [0x02A33],
+ "smeparsl" => [0x029E4],
+ "smid" => [0x02223],
+ "smile" => [0x02323],
+ "smt" => [0x02AAA],
+ "smte" => [0x02AAC],
+ "smtes" => [0x02AAC, 0x0FE00],
+ "softcy" => [0x0044C],
+ "sol" => [0x0002F],
+ "solb" => [0x029C4],
+ "solbar" => [0x0233F],
+ "sopf" => [0x1D564],
+ "spades" => [0x02660],
+ "spadesuit" => [0x02660],
+ "spar" => [0x02225],
+ "sqcap" => [0x02293],
+ "sqcaps" => [0x02293, 0x0FE00],
+ "sqcup" => [0x02294],
+ "sqcups" => [0x02294, 0x0FE00],
+ "sqsub" => [0x0228F],
+ "sqsube" => [0x02291],
+ "sqsubset" => [0x0228F],
+ "sqsubseteq" => [0x02291],
+ "sqsup" => [0x02290],
+ "sqsupe" => [0x02292],
+ "sqsupset" => [0x02290],
+ "sqsupseteq" => [0x02292],
+ "squ" => [0x025A1],
+ "square" => [0x025A1],
+ "squarf" => [0x025AA],
+ "squf" => [0x025AA],
+ "srarr" => [0x02192],
+ "sscr" => [0x1D4C8],
+ "ssetmn" => [0x02216],
+ "ssmile" => [0x02323],
+ "sstarf" => [0x022C6],
+ "star" => [0x02606],
+ "starf" => [0x02605],
+ "straightepsilon" => [0x003F5],
+ "straightphi" => [0x003D5],
+ "strns" => [0x000AF],
+ "sub" => [0x02282],
+ "subE" => [0x02AC5],
+ "subdot" => [0x02ABD],
+ "sube" => [0x02286],
+ "subedot" => [0x02AC3],
+ "submult" => [0x02AC1],
+ "subnE" => [0x02ACB],
+ "subne" => [0x0228A],
+ "subplus" => [0x02ABF],
+ "subrarr" => [0x02979],
+ "subset" => [0x02282],
+ "subseteq" => [0x02286],
+ "subseteqq" => [0x02AC5],
+ "subsetneq" => [0x0228A],
+ "subsetneqq" => [0x02ACB],
+ "subsim" => [0x02AC7],
+ "subsub" => [0x02AD5],
+ "subsup" => [0x02AD3],
+ "succ" => [0x0227B],
+ "succapprox" => [0x02AB8],
+ "succcurlyeq" => [0x0227D],
+ "succeq" => [0x02AB0],
+ "succnapprox" => [0x02ABA],
+ "succneqq" => [0x02AB6],
+ "succnsim" => [0x022E9],
+ "succsim" => [0x0227F],
+ "sum" => [0x02211],
+ "sung" => [0x0266A],
+ "sup" => [0x02283],
+ "sup1" => [0x000B9],
+ "sup2" => [0x000B2],
+ "sup3" => [0x000B3],
+ "supE" => [0x02AC6],
+ "supdot" => [0x02ABE],
+ "supdsub" => [0x02AD8],
+ "supe" => [0x02287],
+ "supedot" => [0x02AC4],
+ "suphsol" => [0x027C9],
+ "suphsub" => [0x02AD7],
+ "suplarr" => [0x0297B],
+ "supmult" => [0x02AC2],
+ "supnE" => [0x02ACC],
+ "supne" => [0x0228B],
+ "supplus" => [0x02AC0],
+ "supset" => [0x02283],
+ "supseteq" => [0x02287],
+ "supseteqq" => [0x02AC6],
+ "supsetneq" => [0x0228B],
+ "supsetneqq" => [0x02ACC],
+ "supsim" => [0x02AC8],
+ "supsub" => [0x02AD4],
+ "supsup" => [0x02AD6],
+ "swArr" => [0x021D9],
+ "swarhk" => [0x02926],
+ "swarr" => [0x02199],
+ "swarrow" => [0x02199],
+ "swnwar" => [0x0292A],
+ "szlig" => [0x000DF],
+ "target" => [0x02316],
+ "tau" => [0x003C4],
+ "tbrk" => [0x023B4],
+ "tcaron" => [0x00165],
+ "tcedil" => [0x00163],
+ "tcy" => [0x00442],
+ "tdot" => [0x020DB],
+ "telrec" => [0x02315],
+ "tfr" => [0x1D531],
+ "there4" => [0x02234],
+ "therefore" => [0x02234],
+ "theta" => [0x003B8],
+ "thetasym" => [0x003D1],
+ "thetav" => [0x003D1],
+ "thickapprox" => [0x02248],
+ "thicksim" => [0x0223C],
+ "thinsp" => [0x02009],
+ "thkap" => [0x02248],
+ "thksim" => [0x0223C],
+ "thorn" => [0x000FE],
+ "tilde" => [0x002DC],
+ "times" => [0x000D7],
+ "timesb" => [0x022A0],
+ "timesbar" => [0x02A31],
+ "timesd" => [0x02A30],
+ "tint" => [0x0222D],
+ "toea" => [0x02928],
+ "top" => [0x022A4],
+ "topbot" => [0x02336],
+ "topcir" => [0x02AF1],
+ "topf" => [0x1D565],
+ "topfork" => [0x02ADA],
+ "tosa" => [0x02929],
+ "tprime" => [0x02034],
+ "trade" => [0x02122],
+ "triangle" => [0x025B5],
+ "triangledown" => [0x025BF],
+ "triangleleft" => [0x025C3],
+ "trianglelefteq" => [0x022B4],
+ "triangleq" => [0x0225C],
+ "triangleright" => [0x025B9],
+ "trianglerighteq" => [0x022B5],
+ "tridot" => [0x025EC],
+ "trie" => [0x0225C],
+ "triminus" => [0x02A3A],
+ "triplus" => [0x02A39],
+ "trisb" => [0x029CD],
+ "tritime" => [0x02A3B],
+ "trpezium" => [0x023E2],
+ "tscr" => [0x1D4C9],
+ "tscy" => [0x00446],
+ "tshcy" => [0x0045B],
+ "tstrok" => [0x00167],
+ "twixt" => [0x0226C],
+ "twoheadleftarrow" => [0x0219E],
+ "twoheadrightarrow" => [0x021A0],
+ "uArr" => [0x021D1],
+ "uHar" => [0x02963],
+ "uacute" => [0x000FA],
+ "uarr" => [0x02191],
+ "ubrcy" => [0x0045E],
+ "ubreve" => [0x0016D],
+ "ucirc" => [0x000FB],
+ "ucy" => [0x00443],
+ "udarr" => [0x021C5],
+ "udblac" => [0x00171],
+ "udhar" => [0x0296E],
+ "ufisht" => [0x0297E],
+ "ufr" => [0x1D532],
+ "ugrave" => [0x000F9],
+ "uharl" => [0x021BF],
+ "uharr" => [0x021BE],
+ "uhblk" => [0x02580],
+ "ulcorn" => [0x0231C],
+ "ulcorner" => [0x0231C],
+ "ulcrop" => [0x0230F],
+ "ultri" => [0x025F8],
+ "umacr" => [0x0016B],
+ "uml" => [0x000A8],
+ "uogon" => [0x00173],
+ "uopf" => [0x1D566],
+ "uparrow" => [0x02191],
+ "updownarrow" => [0x02195],
+ "upharpoonleft" => [0x021BF],
+ "upharpoonright" => [0x021BE],
+ "uplus" => [0x0228E],
+ "upsi" => [0x003C5],
+ "upsih" => [0x003D2],
+ "upsilon" => [0x003C5],
+ "upuparrows" => [0x021C8],
+ "urcorn" => [0x0231D],
+ "urcorner" => [0x0231D],
+ "urcrop" => [0x0230E],
+ "uring" => [0x0016F],
+ "urtri" => [0x025F9],
+ "uscr" => [0x1D4CA],
+ "utdot" => [0x022F0],
+ "utilde" => [0x00169],
+ "utri" => [0x025B5],
+ "utrif" => [0x025B4],
+ "uuarr" => [0x021C8],
+ "uuml" => [0x000FC],
+ "uwangle" => [0x029A7],
+ "vArr" => [0x021D5],
+ "vBar" => [0x02AE8],
+ "vBarv" => [0x02AE9],
+ "vDash" => [0x022A8],
+ "vangrt" => [0x0299C],
+ "varepsilon" => [0x003F5],
+ "varkappa" => [0x003F0],
+ "varnothing" => [0x02205],
+ "varphi" => [0x003D5],
+ "varpi" => [0x003D6],
+ "varpropto" => [0x0221D],
+ "varr" => [0x02195],
+ "varrho" => [0x003F1],
+ "varsigma" => [0x003C2],
+ "varsubsetneq" => [0x0228A, 0x0FE00],
+ "varsubsetneqq" => [0x02ACB, 0x0FE00],
+ "varsupsetneq" => [0x0228B, 0x0FE00],
+ "varsupsetneqq" => [0x02ACC, 0x0FE00],
+ "vartheta" => [0x003D1],
+ "vartriangleleft" => [0x022B2],
+ "vartriangleright" => [0x022B3],
+ "vcy" => [0x00432],
+ "vdash" => [0x022A2],
+ "vee" => [0x02228],
+ "veebar" => [0x022BB],
+ "veeeq" => [0x0225A],
+ "vellip" => [0x022EE],
+ "verbar" => [0x0007C],
+ "vert" => [0x0007C],
+ "vfr" => [0x1D533],
+ "vltri" => [0x022B2],
+ "vnsub" => [0x02282, 0x020D2],
+ "vnsup" => [0x02283, 0x020D2],
+ "vopf" => [0x1D567],
+ "vprop" => [0x0221D],
+ "vrtri" => [0x022B3],
+ "vscr" => [0x1D4CB],
+ "vsubnE" => [0x02ACB, 0x0FE00],
+ "vsubne" => [0x0228A, 0x0FE00],
+ "vsupnE" => [0x02ACC, 0x0FE00],
+ "vsupne" => [0x0228B, 0x0FE00],
+ "vzigzag" => [0x0299A],
+ "wcirc" => [0x00175],
+ "wedbar" => [0x02A5F],
+ "wedge" => [0x02227],
+ "wedgeq" => [0x02259],
+ "weierp" => [0x02118],
+ "wfr" => [0x1D534],
+ "wopf" => [0x1D568],
+ "wp" => [0x02118],
+ "wr" => [0x02240],
+ "wreath" => [0x02240],
+ "wscr" => [0x1D4CC],
+ "xcap" => [0x022C2],
+ "xcirc" => [0x025EF],
+ "xcup" => [0x022C3],
+ "xdtri" => [0x025BD],
+ "xfr" => [0x1D535],
+ "xhArr" => [0x027FA],
+ "xharr" => [0x027F7],
+ "xi" => [0x003BE],
+ "xlArr" => [0x027F8],
+ "xlarr" => [0x027F5],
+ "xmap" => [0x027FC],
+ "xnis" => [0x022FB],
+ "xodot" => [0x02A00],
+ "xopf" => [0x1D569],
+ "xoplus" => [0x02A01],
+ "xotime" => [0x02A02],
+ "xrArr" => [0x027F9],
+ "xrarr" => [0x027F6],
+ "xscr" => [0x1D4CD],
+ "xsqcup" => [0x02A06],
+ "xuplus" => [0x02A04],
+ "xutri" => [0x025B3],
+ "xvee" => [0x022C1],
+ "xwedge" => [0x022C0],
+ "yacute" => [0x000FD],
+ "yacy" => [0x0044F],
+ "ycirc" => [0x00177],
+ "ycy" => [0x0044B],
+ "yen" => [0x000A5],
+ "yfr" => [0x1D536],
+ "yicy" => [0x00457],
+ "yopf" => [0x1D56A],
+ "yscr" => [0x1D4CE],
+ "yucy" => [0x0044E],
+ "yuml" => [0x000FF],
+ "zacute" => [0x0017A],
+ "zcaron" => [0x0017E],
+ "zcy" => [0x00437],
+ "zdot" => [0x0017C],
+ "zeetrf" => [0x02128],
+ "zeta" => [0x003B6],
+ "zfr" => [0x1D537],
+ "zhcy" => [0x00436],
+ "zigrarr" => [0x021DD],
+ "zopf" => [0x1D56B],
+ "zscr" => [0x1D4CF],
+ "zwj" => [0x0200D],
+ "zwnj" => [0x0200C],
+}
+
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
new file mode 100644
index 0000000000..cd4cb52335
--- /dev/null
+++ b/lib/rdoc/markdown/literals.rb
@@ -0,0 +1,416 @@
+# coding: UTF-8
+# :markup: markdown
+
+##
+#--
+# This set of literals is for Ruby 1.9 regular expressions and gives full
+# unicode support.
+#
+# Unlike peg-markdown, this set of literals recognizes Unicode alphanumeric
+# characters, newlines and spaces.
+class RDoc::Markdown::Literals
+ # :stopdoc:
+
+ # This is distinct from setup_parser so that a standalone parser
+ # can redefine #initialize and still have access to the proper
+ # parser setup code.
+ def initialize(str, debug=false)
+ setup_parser(str, debug)
+ end
+
+
+
+ # Prepares for parsing +str+. If you define a custom initialize you must
+ # call this method before #parse
+ def setup_parser(str, debug=false)
+ set_string str, 0
+ @memoizations = Hash.new { |h,k| h[k] = {} }
+ @result = nil
+ @failed_rule = nil
+ @failing_rule_offset = -1
+
+ setup_foreign_grammar
+ end
+
+ attr_reader :string
+ attr_reader :failing_rule_offset
+ attr_accessor :result, :pos
+
+ def current_column(target=pos)
+ if c = string.rindex("\n", target-1)
+ return target - c - 1
+ end
+
+ target + 1
+ end
+
+ def current_line(target=pos)
+ cur_offset = 0
+ cur_line = 0
+
+ string.each_line do |line|
+ cur_line += 1
+ cur_offset += line.size
+ return cur_line if cur_offset >= target
+ end
+
+ -1
+ end
+
+ def lines
+ lines = []
+ string.each_line { |l| lines << l }
+ lines
+ end
+
+
+
+ def get_text(start)
+ @string[start..@pos-1]
+ end
+
+ # Sets the string and current parsing position for the parser.
+ def set_string string, pos
+ @string = string
+ @string_size = string ? string.size : 0
+ @pos = pos
+ end
+
+ def show_pos
+ width = 10
+ if @pos < width
+ "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
+ else
+ "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
+ end
+ end
+
+ def failure_info
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
+ else
+ "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
+ end
+ end
+
+ def failure_caret
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ line = lines[l-1]
+ "#{line}\n#{' ' * (c - 1)}^"
+ end
+
+ def failure_character
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+ lines[l-1][c-1, 1]
+ end
+
+ def failure_oneline
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ char = lines[l-1][c-1, 1]
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ else
+ "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ end
+ end
+
+ class ParseError < RuntimeError
+ end
+
+ def raise_error
+ raise ParseError, failure_oneline
+ end
+
+ def show_error(io=STDOUT)
+ error_pos = @failing_rule_offset
+ line_no = current_line(error_pos)
+ col_no = current_column(error_pos)
+
+ io.puts "On line #{line_no}, column #{col_no}:"
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
+ else
+ io.puts "Failed to match rule '#{@failed_rule}'"
+ end
+
+ io.puts "Got: #{string[error_pos,1].inspect}"
+ line = lines[line_no-1]
+ io.puts "=> #{line}"
+ io.print(" " * (col_no + 3))
+ io.puts "^"
+ end
+
+ def set_failed_rule(name)
+ if @pos > @failing_rule_offset
+ @failed_rule = name
+ @failing_rule_offset = @pos
+ end
+ end
+
+ attr_reader :failed_rule
+
+ def match_string(str)
+ len = str.size
+ if @string[pos,len] == str
+ @pos += len
+ return str
+ end
+
+ return nil
+ end
+
+ def scan(reg)
+ if m = reg.match(@string[@pos..-1])
+ width = m.end(0)
+ @pos += width
+ return true
+ end
+
+ return nil
+ end
+
+ if "".respond_to? :ord
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
+
+ s = @string[@pos].ord
+ @pos += 1
+ s
+ end
+ else
+ def get_byte
+ if @pos >= @string_size
+ return nil
+ end
+
+ s = @string[@pos]
+ @pos += 1
+ s
+ end
+ end
+
+ def parse(rule=nil)
+ # We invoke the rules indirectly via apply
+ # instead of by just calling them as methods because
+ # if the rules use left recursion, apply needs to
+ # manage that.
+
+ if !rule
+ apply(:_root)
+ else
+ method = rule.gsub("-","_hyphen_")
+ apply :"_#{method}"
+ end
+ end
+
+ class MemoEntry
+ def initialize(ans, pos)
+ @ans = ans
+ @pos = pos
+ @result = nil
+ @set = false
+ @left_rec = false
+ end
+
+ attr_reader :ans, :pos, :result, :set
+ attr_accessor :left_rec
+
+ def move!(ans, pos, result)
+ @ans = ans
+ @pos = pos
+ @result = result
+ @set = true
+ @left_rec = false
+ end
+ end
+
+ def external_invoke(other, rule, *args)
+ old_pos = @pos
+ old_string = @string
+
+ set_string other.string, other.pos
+
+ begin
+ if val = __send__(rule, *args)
+ other.pos = @pos
+ other.result = @result
+ else
+ other.set_failed_rule "#{self.class}##{rule}"
+ end
+ val
+ ensure
+ set_string old_string, old_pos
+ end
+ end
+
+ def apply_with_args(rule, *args)
+ memo_key = [rule, args]
+ if m = @memoizations[memo_key][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[memo_key][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule, *args
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, args, start_pos, m)
+ else
+ return ans
+ end
+ end
+ end
+
+ def apply(rule)
+ if m = @memoizations[rule][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[rule][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, nil, start_pos, m)
+ else
+ return ans
+ end
+ end
+ end
+
+ def grow_lr(rule, args, start_pos, m)
+ while true
+ @pos = start_pos
+ @result = m.result
+
+ if args
+ ans = __send__ rule, *args
+ else
+ ans = __send__ rule
+ end
+ return nil unless ans
+
+ break if @pos <= m.pos
+
+ m.move! ans, @pos, @result
+ end
+
+ @result = m.result
+ @pos = m.pos
+ return m.ans
+ end
+
+ class RuleInfo
+ def initialize(name, rendered)
+ @name = name
+ @rendered = rendered
+ end
+
+ attr_reader :name, :rendered
+ end
+
+ def self.rule_info(name, rendered)
+ RuleInfo.new(name, rendered)
+ end
+
+
+ # :startdoc:
+ # :stopdoc:
+ def setup_foreign_grammar; end
+
+ # Alphanumeric = /\p{Word}/
+ def _Alphanumeric
+ _tmp = scan(/\A(?-mix:\p{Word})/)
+ set_failed_rule :_Alphanumeric unless _tmp
+ return _tmp
+ end
+
+ # AlphanumericAscii = /[A-Za-z0-9]/
+ def _AlphanumericAscii
+ _tmp = scan(/\A(?-mix:[A-Za-z0-9])/)
+ set_failed_rule :_AlphanumericAscii unless _tmp
+ return _tmp
+ end
+
+ # BOM = "uFEFF"
+ def _BOM
+ _tmp = match_string("uFEFF")
+ set_failed_rule :_BOM unless _tmp
+ return _tmp
+ end
+
+ # Newline = /\n|\r\n?|\p{Zl}|\p{Zp}/
+ def _Newline
+ _tmp = scan(/\A(?-mix:\n|\r\n?|\p{Zl}|\p{Zp})/)
+ set_failed_rule :_Newline unless _tmp
+ return _tmp
+ end
+
+ # NonAlphanumeric = /\p{^Word}/
+ def _NonAlphanumeric
+ _tmp = scan(/\A(?-mix:\p{^Word})/)
+ set_failed_rule :_NonAlphanumeric unless _tmp
+ return _tmp
+ end
+
+ # Spacechar = /\t|\p{Zs}/
+ def _Spacechar
+ _tmp = scan(/\A(?-mix:\t|\p{Zs})/)
+ set_failed_rule :_Spacechar unless _tmp
+ return _tmp
+ end
+
+ Rules = {}
+ Rules[:_Alphanumeric] = rule_info("Alphanumeric", "/\\p{Word}/")
+ Rules[:_AlphanumericAscii] = rule_info("AlphanumericAscii", "/[A-Za-z0-9]/")
+ Rules[:_BOM] = rule_info("BOM", "\"uFEFF\"")
+ Rules[:_Newline] = rule_info("Newline", "/\\n|\\r\\n?|\\p{Zl}|\\p{Zp}/")
+ Rules[:_NonAlphanumeric] = rule_info("NonAlphanumeric", "/\\p{^Word}/")
+ Rules[:_Spacechar] = rule_info("Spacechar", "/\\t|\\p{Zs}/")
+ # :startdoc:
+end
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
new file mode 100644
index 0000000000..08ecc6f7df
--- /dev/null
+++ b/lib/rdoc/markup.rb
@@ -0,0 +1,870 @@
+# frozen_string_literal: true
+##
+# 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 and other markup formats do no output formatting, this is
+# handled by the RDoc::Markup::Formatter subclasses.
+#
+# = Supported Formats
+#
+# Besides the RDoc::Markup format, the following formats are built in to RDoc:
+#
+# markdown::
+# The markdown format as described by
+# http://daringfireball.net/projects/markdown/. See RDoc::Markdown for
+# details on the parser and supported extensions.
+# rd::
+# The rdtool format. See RDoc::RD for details on the parser and format.
+# tomdoc::
+# The TomDoc format as described by http://tomdoc.org/. See RDoc::TomDoc
+# for details on the parser and supported extensions.
+#
+# You can choose a markup format using the following methods:
+#
+# per project::
+# If you build your documentation with rake use RDoc::Task#markup.
+#
+# If you build your documentation by hand run:
+#
+# rdoc --markup your_favorite_format --write-options
+#
+# and commit <tt>.rdoc_options</tt> and ship it with your packaged gem.
+# per file::
+# At the top of the file use the <tt>:markup:</tt> directive to set the
+# default format for the rest of the file.
+# per comment::
+# Use the <tt>:markup:</tt> directive at the top of a comment you want
+# to write in a different format.
+#
+# = RDoc::Markup
+#
+# RDoc::Markup is extensible at runtime: you can add \new markup elements to
+# be recognized 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 formatting RDoc style comment blocks,
+# Wiki entries, and online FAQs.
+#
+# == Synopsis
+#
+# This code converts +input_string+ to HTML. The conversion takes place in
+# the +convert+ method, so you can use the same RDoc::Markup converter to
+# convert multiple input strings.
+#
+# require 'rdoc'
+#
+# h = RDoc::Markup::ToHtml.new(RDoc::Options.new)
+#
+# puts h.convert(input_string)
+#
+# You can extend the RDoc::Markup parser to recognize new markup
+# sequences, and to add special processing for text that matches a
+# regular expression. Here we make WikiWords significant to the parser,
+# and also make the sequences {word} and \<no>text...</no> signify
+# strike-through text. We then subclass the HTML output class to deal
+# with these:
+#
+# require 'rdoc'
+#
+# class WikiHtml < RDoc::Markup::ToHtml
+# def handle_special_WIKIWORD(special)
+# "<font color=red>" + special.text + "</font>"
+# end
+# end
+#
+# markup = RDoc::Markup.new
+# markup.add_word_pair("{", "}", :STRIKE)
+# markup.add_html("no", :STRIKE)
+#
+# markup.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+#
+# wh = WikiHtml.new RDoc::Options.new, markup
+# wh.add_tag(:STRIKE, "<strike>", "</strike>")
+#
+# puts "<body>#{wh.convert ARGF.read}</body>"
+#
+# == Encoding
+#
+# Where Encoding support is available, RDoc will automatically convert all
+# documents to the same output encoding. The output encoding can be set via
+# RDoc::Options#encoding and defaults to Encoding.default_external.
+#
+# = \RDoc Markup Reference
+#
+# == Block Markup
+#
+# === Paragraphs and Verbatim
+#
+# The markup engine 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. Empty lines separate paragraphs.
+#
+# Any line that starts to the right of the current margin is treated
+# as verbatim text. This is useful for code listings:
+#
+# 3.times { puts "Ruby" }
+#
+# In verbatim text, two or more blank lines are collapsed into one,
+# and trailing blank lines are removed:
+#
+# This is the first line
+#
+#
+# This is the second non-blank line,
+# after 2 blank lines in the source markup.
+#
+#
+# There were two trailing blank lines right above this paragraph, that
+# have been removed. In addition, the verbatim text has been shifted
+# left, so the amount of indentation of verbatim text is unimportant.
+#
+# For HTML output RDoc makes a small effort to determine if a verbatim section
+# contains Ruby source code. If so, the verbatim block will be marked up as
+# HTML. Triggers include "def", "class", "module", "require", the "hash
+# rocket"# (=>) or a block call with a parameter.
+#
+# === Headers
+#
+# A line starting with an equal sign (=) is treated as a
+# heading. Level one headings have one equals sign, level two headings
+# have two, and so on until level six, which is the maximum
+# (seven hyphens or more result in a level six heading).
+#
+# For example, the above header was obtained with:
+#
+# === Headers
+#
+# In HTML output headers have an id matching their name. The above example's
+# HTML is:
+#
+# <h3 id="label-Headers">Headers</h3>
+#
+# If a heading is inside a method body the id will be prefixed with the
+# method's id. If the above header where in the documentation for a method
+# such as:
+#
+# ##
+# # This method does fun things
+# #
+# # = Example
+# #
+# # Example of fun things goes here ...
+#
+# def do_fun_things
+# end
+#
+# The header's id would be:
+#
+# <h1 id="method-i-do_fun_things-label-Example">Example</h1>
+#
+# The label can be linked-to using <tt>SomeClass@Headers</tt>. See
+# {Links}[RDoc::Markup@Links] for further details.
+#
+# === Rules
+#
+# A line starting with three or more hyphens (at the current indent)
+# generates a horizontal rule.
+#
+# ---
+#
+# produces:
+#
+# ---
+#
+# === Simple Lists
+#
+# If a paragraph starts with a "*", "-", "<digit>." or "<letter>.",
+# then it is taken to be the start of a list. The margin is 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
+#
+# produces:
+#
+# * 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
+#
+# === Labeled Lists
+#
+# 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
+#
+# produces:
+#
+# [cat] a small furry mammal
+# that seems to sleep a lot
+#
+# [ant] a little insect that is known
+# to enjoy picnics
+#
+# If you want the list bodies to line up to the left of the labels,
+# use two colons:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# produces:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# Notice that blank lines right after the label are ignored in labeled lists:
+#
+# [one]
+#
+# definition 1
+#
+# [two]
+#
+# definition 2
+#
+# produces the same output as
+#
+# [one] definition 1
+# [two] definition 2
+#
+#
+# === Lists and Verbatim
+#
+# If you want to introduce a verbatim section right after a list, it has to be
+# less indented than the list item bodies, but more indented than the list
+# label, letter, digit or bullet. For instance:
+#
+# * point 1
+#
+# * point 2, first paragraph
+#
+# point 2, second paragraph
+# verbatim text inside point 2
+# point 2, third paragraph
+# verbatim text outside of the list (the list is therefore closed)
+# regular paragraph after the list
+#
+# produces:
+#
+# * point 1
+#
+# * point 2, first paragraph
+#
+# point 2, second paragraph
+# verbatim text inside point 2
+# point 2, third paragraph
+# verbatim text outside of the list (the list is therefore closed)
+# regular paragraph after the list
+#
+# == Text Markup
+#
+# === Bold, Italic, Typewriter Text
+#
+# 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:
+#
+# <tt>\*_word_\*</tt>:: displays _word_ in a *bold* font
+# <tt>\__word_\_</tt>:: displays _word_ in an _emphasized_ font
+# <tt>\+_word_\+</tt>:: displays _word_ in a +code+ font
+#
+# General markup affects text between a start delimiter and an end
+# delimiter. Not surprisingly, these delimiters look like HTML markup.
+#
+# <tt>\<b>_text_</b></tt>:: displays _text_ in a *bold* font
+# <tt>\<em>_text_</em></tt>:: displays _text_ in an _emphasized_ font
+# (alternate tag: <tt>\<i></tt>)
+# <tt>\<tt>_text_\</tt></tt>:: displays _text_ in a +code+ font
+# (alternate tag: <tt>\<code></tt>)
+#
+# 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 (see <i>Escaping
+# Text Markup</i>, below).
+#
+# === Links
+#
+# Links to starting with +http:+, +https:+, +mailto:+, +ftp:+ or +www.+
+# are recognized. An HTTP url that references an external image is converted
+# into an inline image element.
+#
+# Classes and methods will be automatically linked to their definition. For
+# example, <tt>RDoc::Markup</tt> will link to this documentation. By default
+# methods will only be automatically linked if they contain an <tt>_</tt> (all
+# methods can be automatically linked through the <tt>--hyperlink-all</tt>
+# command line option).
+#
+# Single-word methods can be linked by using the <tt>#</tt> character for
+# instance methods or <tt>::</tt> for class methods. For example,
+# <tt>#convert</tt> links to #convert. A class or method may be combined like
+# <tt>RDoc::Markup#convert</tt>.
+#
+# A heading inside the documentation can be linked by following the class
+# or method by an <tt>@</tt> then the heading name.
+# <tt>RDoc::Markup@Links</tt> will link to this section like this:
+# RDoc::Markup@Links. Spaces in headings with multiple words must be escaped
+# with <tt>+</tt> like <tt>RDoc::Markup@Escaping+Text+Markup</tt>.
+# Punctuation and other special characters must be escaped like CGI.escape.
+#
+# The <tt>@</tt> can also be used to link to sections. If a section and a
+# heading share the same name the section is preferred for the link.
+#
+# Links can also be of the form <tt>label[url]</tt>, in which case +label+ is
+# used in the displayed text, and +url+ is used as the target. If +label+
+# contains multiple words, put it in braces: <tt>{multi word label}[url]</tt>.
+# The +url+ may be an +http:+-type link or a cross-reference to a class,
+# module or method with a label.
+#
+# Links with the <code>rdoc-image:</code> scheme will create an image tag for
+# HTML output. Only fully-qualified URLs are supported.
+#
+# Links with the <tt>rdoc-ref:</tt> scheme will link to the referenced class,
+# module, method, file, etc. If the referenced item is does not exist
+# no link will be generated and <tt>rdoc-ref:</tt> will be removed from the
+# resulting text.
+#
+# Links starting with <tt>rdoc-label:label_name</tt> will link to the
+# +label_name+. You can create a label for the current link (for
+# bidirectional links) by supplying a name for the current link like
+# <tt>rdoc-label:label-other:label-mine</tt>.
+#
+# Links starting with +link:+ refer to local files whose path is relative to
+# the <tt>--op</tt> directory. Use <tt>rdoc-ref:</tt> instead of
+# <tt>link:</tt> to link to files generated by RDoc as the link target may
+# be different across RDoc generators.
+#
+# Example links:
+#
+# https://github.com/ruby/rdoc
+# mailto:user@example.com
+# {RDoc Documentation}[http://rdoc.rubyforge.org]
+# {RDoc Markup}[rdoc-ref:RDoc::Markup]
+#
+# === Escaping Text Markup
+#
+# Text markup can be escaped with a backslash, as in \<tt>, which was obtained
+# with <tt>\\<tt></tt>. Except in verbatim sections and between \<tt> tags,
+# to produce a backslash you have to double it unless it is followed by a
+# space, tab or newline. Otherwise, the HTML formatter will discard it, as it
+# is used to escape potential links:
+#
+# * The \ must be doubled if not followed by white space: \\.
+# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
+# * This is a link to {ruby-lang}[www.ruby-lang.org].
+# * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org].
+# * This will not be linked to \RDoc::RDoc#document
+#
+# generates:
+#
+# * The \ must be doubled if not followed by white space: \\.
+# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
+# * This is a link to {ruby-lang}[www.ruby-lang.org]
+# * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org]
+# * This will not be linked to \RDoc::RDoc#document
+#
+# Inside \<tt> tags, more precisely, leading backslashes are removed only if
+# followed by a markup character (<tt><*_+</tt>), a backslash, or a known link
+# reference (a known class or method). So in the example above, the backslash
+# of <tt>\S</tt> would be removed if there was a class or module named +S+ in
+# the current context.
+#
+# This behavior is inherited from RDoc version 1, and has been kept for
+# compatibility with existing RDoc documentation.
+#
+# === Conversion of characters
+#
+# HTML will convert two/three dashes to an em-dash. Other common characters are
+# converted as well:
+#
+# em-dash:: -- or ---
+# ellipsis:: ...
+#
+# single quotes:: 'text' or `text'
+# double quotes:: "text" or ``text''
+#
+# copyright:: (c)
+# registered trademark:: (r)
+#
+# produces:
+#
+# em-dash:: -- or ---
+# ellipsis:: ...
+#
+# single quotes:: 'text' or `text'
+# double quotes:: "text" or ``text''
+#
+# copyright:: (c)
+# registered trademark:: (r)
+#
+#
+# == Documenting Source Code
+#
+# Comment blocks can be written fairly naturally, either using <tt>#</tt> on
+# successive lines of the comment, or by including the comment in
+# a <tt>=begin</tt>/<tt>=end</tt> block. If you use the latter form,
+# the <tt>=begin</tt> 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 starting
+# with <tt>--</tt> right after the <tt>#</tt> character (otherwise,
+# it will be treated as a rule if it has three dashes or more).
+# This can be used to separate external from internal comments,
+# or to stop a comment being associated with a method, class, or module.
+# Commenting can be turned back on with a line that starts with <tt>++</tt>.
+#
+# ##
+# # Extract the age and calculate the date-of-birth.
+# #--
+# # FIXME: fails if the birthday falls on February 29th
+# #++
+# # The DOB is returned as a Time object.
+#
+# def get_dob(person)
+# # ...
+# end
+#
+# Names of classes, files, and any method names containing an underscore or
+# preceded by a hash character are automatically linked from comment text to
+# their description. This linking works inside the current class or module,
+# and with ancestor methods (in included modules or in the superclass).
+#
+# 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.
+#
+# RDoc automatically cross-references words with underscores or camel-case.
+# To suppress cross-references, prefix the word with a \ character. To
+# include special characters like "<tt>\n</tt>", you'll need to use
+# two \ characters in normal text, but only one in \<tt> text:
+#
+# "\\n" or "<tt>\n</tt>"
+#
+# produces:
+#
+# "\\n" or "<tt>\n</tt>"
+#
+# == Directives
+#
+# Directives are keywords surrounded by ":" characters.
+#
+# === Controlling what is documented
+#
+# [+:nodoc:+ / <tt>:nodoc: all</tt>]
+# This directive prevents documentation for the element from
+# being generated. For classes and modules, methods, aliases,
+# constants, and attributes directly within the affected class or
+# module also will be omitted. By default, though, modules and
+# classes within that class or 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 <tt>MyModule::Input</tt> will be documented.
+#
+# The +:nodoc:+ directive, like +:enddoc:+, +:stopdoc:+ and +:startdoc:+
+# presented below, is local to the current file: if you do not want to
+# document a module that appears in several files, specify +:nodoc:+ on each
+# appearance, at least once per file.
+#
+# [+:stopdoc:+ / +:startdoc:+]
+# Stop and start adding new documentation elements to the current container.
+# For example, if a class has a number of constants that you don't want to
+# document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
+# last. If you don't specify a +:startdoc:+ by the end of the container,
+# disables documentation for the rest of the current file.
+#
+# [+:doc:+]
+# Forces a method or attribute to be documented even if it wouldn't be
+# otherwise. Useful if, for example, you want to include documentation of a
+# particular private method.
+#
+# [+:enddoc:+]
+# Document nothing further at the current level: directives +:startdoc:+ and
+# +:doc:+ that appear after this will not be honored for the current container
+# (file, class or module), in the current file.
+#
+# [+:notnew:+ / +:not_new:+ / +:not-new:+ ]
+# 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:+ directive stops this. Remember that +initialize+ is private,
+# so you won't see the documentation unless you use the +-a+ command line
+# option.
+#
+# === Method arguments
+#
+# [+:arg:+ or +:args:+ _parameters_]
+# Overrides the default argument handling with exactly these parameters.
+#
+# ##
+# # :args: a, b
+#
+# def some_method(*a)
+# end
+#
+# [+:yield:+ or +:yields:+ _parameters_]
+# Overrides the default yield discovery with these parameters.
+#
+# ##
+# # :yields: key, value
+#
+# def each_thing &block
+# @things.each(&block)
+# end
+#
+# [+:call-seq:+]
+# Lines up to the next blank line or lines with a common prefix in the
+# comment are treated as the method's calling sequence, overriding the
+# default parsing of method parameters and yield arguments.
+#
+# Multiple lines may be used.
+#
+# # :call-seq:
+# # ARGF.readlines(sep=$/) -> array
+# # ARGF.readlines(limit) -> array
+# # ARGF.readlines(sep, limit) -> array
+# #
+# # ARGF.to_a(sep=$/) -> array
+# # ARGF.to_a(limit) -> array
+# # ARGF.to_a(sep, limit) -> array
+# #
+# # The remaining lines are documentation ...
+#
+# === Sections
+#
+# Sections allow you to group methods in a class into sensible containers. If
+# you use the sections 'Public', 'Internal' and 'Deprecated' (the three
+# allowed method statuses from TomDoc) the sections will be displayed in that
+# order placing the most useful methods at the top. Otherwise, sections will
+# be displayed in alphabetical order.
+#
+# [+:category:+ _section_]
+# Adds this item to the named +section+ overriding the current section. Use
+# this to group methods by section in RDoc output while maintaining a
+# sensible ordering (like alphabetical).
+#
+# # :category: Utility Methods
+# #
+# # CGI escapes +text+
+#
+# def convert_string text
+# CGI.escapeHTML text
+# end
+#
+# An empty category will place the item in the default category:
+#
+# # :category:
+# #
+# # This method is in the default category
+#
+# def some_method
+# # ...
+# end
+#
+# Unlike the :section: directive, :category: is not sticky. The category
+# only applies to the item immediately following the comment.
+#
+# Use the :section: directive to provide introductory text for a section of
+# documentation.
+#
+# [+:section:+ _title_]
+# Provides section introductory text in RDoc output. The title following
+# +:section:+ is used as the section name and the remainder of the comment
+# containing the section is used as introductory text. A section's comment
+# block must be separated from following comment blocks. Use an empty title
+# to switch to the default section.
+#
+# The :section: directive is sticky, so subsequent methods, aliases,
+# attributes, and classes will be contained in this section until the
+# section is changed. The :category: directive will override the :section:
+# directive.
+#
+# 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 to the
+# section.
+#
+# Example:
+#
+# # ----------------------------------------
+# # :section: My Section
+# # This is the section that I wrote.
+# # See it glisten in the noon-day sun.
+# # ----------------------------------------
+#
+# ##
+# # Comment for some_method
+#
+# def some_method
+# # ...
+# end
+#
+# === Other directives
+#
+# [+:markup:+ _type_]
+# Overrides the default markup type for this comment with the specified
+# markup type. For Ruby files, if the first comment contains this directive
+# it is applied automatically to all comments in the file.
+#
+# Unless you are converting between markup formats you should use a
+# <code>.rdoc_options</code> file to specify the default documentation
+# format for your entire project. See RDoc::Options@Saved+Options for
+# instructions.
+#
+# At the top of a file the +:markup:+ directive applies to the entire file:
+#
+# # coding: UTF-8
+# # :markup: TomDoc
+#
+# # TomDoc comment here ...
+#
+# class MyClass
+# # ...
+#
+# For just one comment:
+#
+# # ...
+# end
+#
+# # :markup: RDoc
+# #
+# # This is a comment in RDoc markup format ...
+#
+# def some_method
+# # ...
+#
+# See Markup@CONTRIBUTING for instructions on adding a new markup format.
+#
+# [+:include:+ _filename_]
+# Include the contents of the named file at this point. This directive
+# must appear alone on one line, possibly preceded by spaces. In this
+# position, it can be escaped with a \ in front of the first colon.
+#
+# The file will be searched for in the directories listed by the +--include+
+# option, or in the current directory by default. The contents of the file
+# will be shifted to have the same indentation as the ':' at the start of
+# the +:include:+ directive.
+#
+# [+:title:+ _text_]
+# Sets the title for the document. Equivalent to the <tt>--title</tt>
+# command line parameter. (The command line parameter overrides any :title:
+# directive in the source).
+#
+# [+:main:+ _name_]
+# Equivalent to the <tt>--main</tt> command line parameter.
+#
+#--
+# Original Author:: Dave Thomas, dave@pragmaticprogrammer.com
+# License:: Ruby license
+
+class RDoc::Markup
+
+ ##
+ # An AttributeManager which handles inline markup.
+
+ attr_reader :attribute_manager
+
+ ##
+ # Parses +str+ into an RDoc::Markup::Document.
+
+ def self.parse str
+ RDoc::Markup::Parser.parse str
+ rescue RDoc::Markup::Parser::Error => e
+ $stderr.puts <<-EOF
+While parsing markup, RDoc encountered a #{e.class}:
+
+#{e}
+\tfrom #{e.backtrace.join "\n\tfrom "}
+
+---8<---
+#{text}
+---8<---
+
+RDoc #{RDoc::VERSION}
+
+Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} #{RUBY_RELEASE_DATE}
+
+Please file a bug report with the above information at:
+
+https://github.com/ruby/rdoc/issues
+
+ EOF
+ raise
+ end
+
+ ##
+ # Take a block of text and use various heuristics to determine its
+ # structure (paragraphs, lists, and so on). Invoke an event handler as we
+ # identify significant chunks.
+
+ def initialize attribute_manager = nil
+ @attribute_manager = attribute_manager || RDoc::Markup::AttributeManager.new
+ @output = nil
+ end
+
+ ##
+ # Add to the sequences used to add formatting to an individual word (such
+ # as *bold*). Matching entries will generate attributes that the output
+ # formatters can recognize by their +name+.
+
+ def add_word_pair(start, stop, name)
+ @attribute_manager.add_word_pair(start, stop, name)
+ end
+
+ ##
+ # Add to the sequences recognized as general markup.
+
+ def add_html(tag, name)
+ @attribute_manager.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)
+ @attribute_manager.add_special(pattern, name)
+ end
+
+ ##
+ # We take +input+, parse it if necessary, then invoke the output +formatter+
+ # using a Visitor to render the result.
+
+ def convert input, formatter
+ document = case input
+ when RDoc::Markup::Document then
+ input
+ else
+ RDoc::Markup::Parser.parse input
+ end
+
+ document.accept formatter
+ end
+
+ autoload :Parser, 'rdoc/markup/parser'
+ autoload :PreProcess, 'rdoc/markup/pre_process'
+
+ # Inline markup classes
+ autoload :AttrChanger, 'rdoc/markup/attr_changer'
+ autoload :AttrSpan, 'rdoc/markup/attr_span'
+ autoload :Attributes, 'rdoc/markup/attributes'
+ autoload :AttributeManager, 'rdoc/markup/attribute_manager'
+ autoload :Special, 'rdoc/markup/special'
+
+ # RDoc::Markup AST
+ autoload :BlankLine, 'rdoc/markup/blank_line'
+ autoload :BlockQuote, 'rdoc/markup/block_quote'
+ autoload :Document, 'rdoc/markup/document'
+ autoload :HardBreak, 'rdoc/markup/hard_break'
+ autoload :Heading, 'rdoc/markup/heading'
+ autoload :Include, 'rdoc/markup/include'
+ autoload :IndentedParagraph, 'rdoc/markup/indented_paragraph'
+ autoload :List, 'rdoc/markup/list'
+ autoload :ListItem, 'rdoc/markup/list_item'
+ autoload :Paragraph, 'rdoc/markup/paragraph'
+ autoload :Raw, 'rdoc/markup/raw'
+ autoload :Rule, 'rdoc/markup/rule'
+ autoload :Verbatim, 'rdoc/markup/verbatim'
+
+ # Formatters
+ autoload :Formatter, 'rdoc/markup/formatter'
+ autoload :FormatterTestCase, 'rdoc/markup/formatter_test_case'
+ autoload :TextFormatterTestCase, 'rdoc/markup/text_formatter_test_case'
+
+ autoload :ToAnsi, 'rdoc/markup/to_ansi'
+ autoload :ToBs, 'rdoc/markup/to_bs'
+ autoload :ToHtml, 'rdoc/markup/to_html'
+ autoload :ToHtmlCrossref, 'rdoc/markup/to_html_crossref'
+ autoload :ToHtmlSnippet, 'rdoc/markup/to_html_snippet'
+ autoload :ToLabel, 'rdoc/markup/to_label'
+ autoload :ToMarkdown, 'rdoc/markup/to_markdown'
+ autoload :ToRdoc, 'rdoc/markup/to_rdoc'
+ autoload :ToTableOfContents, 'rdoc/markup/to_table_of_contents'
+ autoload :ToTest, 'rdoc/markup/to_test'
+ autoload :ToTtOnly, 'rdoc/markup/to_tt_only'
+
+end
+
diff --git a/lib/rdoc/markup/.document b/lib/rdoc/markup/.document
deleted file mode 100644
index 3cf4f21bd7..0000000000
--- a/lib/rdoc/markup/.document
+++ /dev/null
@@ -1,2 +0,0 @@
-simple_markup
-simple_markup.rb
diff --git a/lib/rdoc/markup/attr_changer.rb b/lib/rdoc/markup/attr_changer.rb
new file mode 100644
index 0000000000..4c4bc6479e
--- /dev/null
+++ b/lib/rdoc/markup/attr_changer.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+class RDoc::Markup
+
+ AttrChanger = Struct.new :turn_on, :turn_off # :nodoc:
+
+end
+
+##
+# An AttrChanger records a change in attributes. It contains a bitmap of the
+# attributes to turn on, and a bitmap of those to turn off.
+
+class RDoc::Markup::AttrChanger
+
+ def to_s # :nodoc:
+ "Attr: +#{turn_on}/-#{turn_off}"
+ end
+
+ def inspect # :nodoc:
+ '+%d/-%d' % [turn_on, turn_off]
+ end
+
+end
+
diff --git a/lib/rdoc/markup/attr_span.rb b/lib/rdoc/markup/attr_span.rb
new file mode 100644
index 0000000000..63aace60d2
--- /dev/null
+++ b/lib/rdoc/markup/attr_span.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+##
+# An array of attributes which parallels the characters in a string.
+
+class RDoc::Markup::AttrSpan
+
+ ##
+ # Creates a new AttrSpan for +length+ characters
+
+ def initialize(length)
+ @attrs = Array.new(length, 0)
+ end
+
+ ##
+ # Toggles +bits+ from +start+ to +length+
+ def set_attrs(start, length, bits)
+ for i in start ... (start+length)
+ @attrs[i] |= bits
+ end
+ end
+
+ ##
+ # Accesses flags for character +n+
+
+ def [](n)
+ @attrs[n]
+ end
+
+end
+
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
new file mode 100644
index 0000000000..a10f731615
--- /dev/null
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -0,0 +1,344 @@
+# frozen_string_literal: true
+##
+# Manages changes of attributes in a block of text
+
+class RDoc::Markup::AttributeManager
+
+ ##
+ # The NUL character
+
+ NULL = "\000".freeze
+
+ #--
+ # We work by substituting non-printing characters in to the text. For now
+ # I'm assuming that I can substitute a character in the range 0..8 for a 7
+ # bit character without damaging the encoded string, but this might be
+ # optimistic
+ #++
+
+ A_PROTECT = 004 # :nodoc:
+
+ ##
+ # Special mask character to prevent inline markup handling
+
+ PROTECT_ATTR = A_PROTECT.chr # :nodoc:
+
+ ##
+ # The attributes enabled for this markup object.
+
+ attr_reader :attributes
+
+ ##
+ # This maps delimiters that occur around words (such as *bold* or +tt+)
+ # where the start and end delimiters and the same. This lets us optimize
+ # the regexp
+
+ attr_reader :matching_word_pairs
+
+ ##
+ # And this is used when the delimiters aren't the same. In this case the
+ # hash maps a pattern to the attribute character
+
+ attr_reader :word_pair_map
+
+ ##
+ # This maps HTML tags to the corresponding attribute char
+
+ attr_reader :html_tags
+
+ ##
+ # A \ in front of a character that would normally be processed turns off
+ # processing. We do this by turning \< into <#{PROTECT}
+
+ attr_reader :protectable
+
+ ##
+ # And this maps _special_ sequences to a name. A special sequence is
+ # something like a WikiWord
+
+ attr_reader :special
+
+ ##
+ # Creates a new attribute manager that understands bold, emphasized and
+ # teletype text.
+
+ def initialize
+ @html_tags = {}
+ @matching_word_pairs = {}
+ @protectable = %w[<]
+ @special = []
+ @word_pair_map = {}
+ @attributes = RDoc::Markup::Attributes.new
+
+ add_word_pair "*", "*", :BOLD
+ add_word_pair "_", "_", :EM
+ add_word_pair "+", "+", :TT
+
+ add_html "em", :EM
+ add_html "i", :EM
+ add_html "b", :BOLD
+ add_html "tt", :TT
+ add_html "code", :TT
+ end
+
+ ##
+ # Return an attribute object with the given turn_on and turn_off bits set
+
+ def attribute(turn_on, turn_off)
+ RDoc::Markup::AttrChanger.new turn_on, turn_off
+ end
+
+ ##
+ # Changes the current attribute from +current+ to +new+
+
+ def change_attribute current, new
+ diff = current ^ new
+ attribute(new & diff, current & diff)
+ end
+
+ ##
+ # Used by the tests to change attributes by name from +current_set+ to
+ # +new_set+
+
+ def changed_attribute_by_name current_set, new_set
+ current = new = 0
+ current_set.each do |name|
+ current |= @attributes.bitmap_for(name)
+ end
+
+ new_set.each do |name|
+ new |= @attributes.bitmap_for(name)
+ end
+
+ change_attribute(current, new)
+ end
+
+ ##
+ # Copies +start_pos+ to +end_pos+ from the current string
+
+ def copy_string(start_pos, end_pos)
+ res = @str[start_pos...end_pos]
+ res.gsub!(/\000/, '')
+ res
+ end
+
+ ##
+ # Map attributes like <b>text</b>to the sequence
+ # \001\002<char>\001\003<char>, where <char> is a per-attribute specific
+ # character
+
+ def convert_attrs(str, attrs)
+ # first do matching ones
+ tags = @matching_word_pairs.keys.join("")
+
+ re = /(^|\W)([#{tags}])([#\\]?[\w:.\/-]+?\S?)\2(\W|$)/
+
+ 1 while str.gsub!(re) do
+ attr = @matching_word_pairs[$2]
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
+ $1 + NULL * $2.length + $3 + NULL * $2.length + $4
+ end
+
+ # then non-matching
+ unless @word_pair_map.empty? then
+ @word_pair_map.each do |regexp, attr|
+ str.gsub!(regexp) {
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ NULL * $1.length + $2 + NULL * $3.length
+ }
+ end
+ end
+ end
+
+ ##
+ # Converts HTML tags to RDoc attributes
+
+ def convert_html(str, attrs)
+ tags = @html_tags.keys.join '|'
+
+ 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
+ attr = @html_tags[$1.downcase]
+ html_length = $1.length + 2
+ seq = NULL * html_length
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
+ seq + $2 + seq + NULL
+ }
+ end
+
+ ##
+ # Converts special sequences to RDoc attributes
+
+ def convert_specials str, attrs
+ @special.each do |regexp, attribute|
+ str.scan(regexp) do
+ capture = $~.size == 1 ? 0 : 1
+
+ s, e = $~.offset capture
+
+ attrs.set_attrs s, e - s, attribute | @attributes.special
+ end
+ end
+ end
+
+ ##
+ # Escapes special sequences of text to prevent conversion to RDoc
+
+ def mask_protected_sequences
+ # protect __send__, __FILE__, etc.
+ @str.gsub!(/__([a-z]+)__/i,
+ "_#{PROTECT_ATTR}_#{PROTECT_ATTR}\\1_#{PROTECT_ATTR}_#{PROTECT_ATTR}")
+ @str.gsub!(/(\A|[^\\])\\([#{Regexp.escape @protectable.join}])/m,
+ "\\1\\2#{PROTECT_ATTR}")
+ @str.gsub!(/\\(\\[#{Regexp.escape @protectable.join}])/m, "\\1")
+ end
+
+ ##
+ # Unescapes special sequences of text
+
+ def unmask_protected_sequences
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
+ end
+
+ ##
+ # Adds a markup class with +name+ for words wrapped in the +start+ and
+ # +stop+ character. To make words wrapped with "*" bold:
+ #
+ # am.add_word_pair '*', '*', :BOLD
+
+ def add_word_pair(start, stop, name)
+ raise ArgumentError, "Word flags may not start with '<'" if
+ start[0,1] == '<'
+
+ bitmap = @attributes.bitmap_for name
+
+ if start == stop then
+ @matching_word_pairs[start] = bitmap
+ else
+ pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/
+ @word_pair_map[pattern] = bitmap
+ end
+
+ @protectable << start[0,1]
+ @protectable.uniq!
+ end
+
+ ##
+ # Adds a markup class with +name+ for words surrounded by HTML tag +tag+.
+ # To process emphasis tags:
+ #
+ # am.add_html 'em', :EM
+
+ def add_html(tag, name)
+ @html_tags[tag.downcase] = @attributes.bitmap_for name
+ end
+
+ ##
+ # Adds a special handler for +pattern+ with +name+. A simple URL handler
+ # would be:
+ #
+ # @am.add_special(/((https?:)\S+\w)/, :HYPERLINK)
+
+ def add_special pattern, name
+ @special << [pattern, @attributes.bitmap_for(name)]
+ end
+
+ ##
+ # Processes +str+ converting attributes, HTML and specials
+
+ def flow str
+ @str = str.dup
+
+ mask_protected_sequences
+
+ @attrs = RDoc::Markup::AttrSpan.new @str.length
+
+ convert_attrs @str, @attrs
+ convert_html @str, @attrs
+ convert_specials @str, @attrs
+
+ unmask_protected_sequences
+
+ split_into_flow
+ end
+
+ ##
+ # Debug method that prints a string along with its attributes
+
+ def display_attributes
+ puts
+ puts @str.tr(NULL, "!")
+ bit = 1
+ 16.times do |bno|
+ line = ""
+ @str.length.times do |i|
+ if (@attrs[i] & bit) == 0
+ line << " "
+ else
+ if bno.zero?
+ line << "S"
+ else
+ line << ("%d" % (bno+1))
+ end
+ end
+ end
+ puts(line) unless line =~ /^ *$/
+ bit <<= 1
+ end
+ end
+
+ ##
+ # Splits the string into chunks by attribute change
+
+ def split_into_flow
+ res = []
+ current_attr = 0
+
+ str_len = @str.length
+
+ # skip leading invisible text
+ i = 0
+ i += 1 while i < str_len and @str[i].chr == "\0"
+ start_pos = i
+
+ # then scan the string, chunking it on attribute changes
+ while i < str_len
+ new_attr = @attrs[i]
+ if new_attr != current_attr
+ if i > start_pos
+ res << copy_string(start_pos, i)
+ start_pos = i
+ end
+
+ res << change_attribute(current_attr, new_attr)
+ current_attr = new_attr
+
+ if (current_attr & @attributes.special) != 0 then
+ i += 1 while
+ i < str_len and (@attrs[i] & @attributes.special) != 0
+
+ res << RDoc::Markup::Special.new(current_attr,
+ copy_string(start_pos, i))
+ start_pos = i
+ next
+ end
+ end
+
+ # move on, skipping any invisible characters
+ begin
+ i += 1
+ end while i < str_len and @str[i].chr == "\0"
+ end
+
+ # tidy up trailing text
+ if start_pos < str_len
+ res << copy_string(start_pos, str_len)
+ end
+
+ # and reset to all attributes off
+ res << change_attribute(current_attr, 0) if current_attr != 0
+
+ res
+ end
+
+end
+
diff --git a/lib/rdoc/markup/attributes.rb b/lib/rdoc/markup/attributes.rb
new file mode 100644
index 0000000000..ec30160d3d
--- /dev/null
+++ b/lib/rdoc/markup/attributes.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+##
+# We manage a set of attributes. Each attribute has a symbol name and a bit
+# value.
+
+class RDoc::Markup::Attributes
+
+ ##
+ # The special attribute type. See RDoc::Markup#add_special
+
+ attr_reader :special
+
+ ##
+ # Creates a new attributes set.
+
+ def initialize
+ @special = 1
+
+ @name_to_bitmap = [
+ [:_SPECIAL_, @special],
+ ]
+
+ @next_bitmap = @special << 1
+ end
+
+ ##
+ # Returns a unique bit for +name+
+
+ def bitmap_for name
+ bitmap = @name_to_bitmap.assoc name
+
+ unless bitmap then
+ bitmap = @next_bitmap
+ @next_bitmap <<= 1
+ @name_to_bitmap << [name, bitmap]
+ else
+ bitmap = bitmap.last
+ end
+
+ bitmap
+ end
+
+ ##
+ # Returns a string representation of +bitmap+
+
+ def as_string bitmap
+ return 'none' if bitmap.zero?
+ res = []
+
+ @name_to_bitmap.each do |name, bit|
+ res << name if (bitmap & bit) != 0
+ end
+
+ res.join ','
+ end
+
+ ##
+ # yields each attribute name in +bitmap+
+
+ def each_name_of bitmap
+ return enum_for __method__, bitmap unless block_given?
+
+ @name_to_bitmap.each do |name, bit|
+ next if bit == @special
+
+ yield name.to_s if (bitmap & bit) != 0
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/blank_line.rb b/lib/rdoc/markup/blank_line.rb
new file mode 100644
index 0000000000..3129ab5e7f
--- /dev/null
+++ b/lib/rdoc/markup/blank_line.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+##
+# An empty line. This class is a singleton.
+
+class RDoc::Markup::BlankLine
+
+ @instance = new
+
+ ##
+ # RDoc::Markup::BlankLine is a singleton
+
+ def self.new
+ @instance
+ end
+
+ ##
+ # Calls #accept_blank_line on +visitor+
+
+ def accept visitor
+ visitor.accept_blank_line self
+ end
+
+ def pretty_print q # :nodoc:
+ q.text 'blankline'
+ end
+
+end
+
diff --git a/lib/rdoc/markup/block_quote.rb b/lib/rdoc/markup/block_quote.rb
new file mode 100644
index 0000000000..7a4b3e36b0
--- /dev/null
+++ b/lib/rdoc/markup/block_quote.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+##
+# A quoted section which contains markup items.
+
+class RDoc::Markup::BlockQuote < RDoc::Markup::Raw
+
+ ##
+ # Calls #accept_block_quote on +visitor+
+
+ def accept visitor
+ visitor.accept_block_quote self
+ end
+
+end
+
diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb
new file mode 100644
index 0000000000..f3a5de1fc3
--- /dev/null
+++ b/lib/rdoc/markup/document.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+##
+# A Document containing lists, headings, paragraphs, etc.
+
+class RDoc::Markup::Document
+
+ include Enumerable
+
+ ##
+ # The file this document was created from. See also
+ # RDoc::ClassModule#add_comment
+
+ attr_reader :file
+
+ ##
+ # If a heading is below the given level it will be omitted from the
+ # table_of_contents
+
+ attr_accessor :omit_headings_below
+
+ ##
+ # The parts of the Document
+
+ attr_reader :parts
+
+ ##
+ # Creates a new Document with +parts+
+
+ def initialize *parts
+ @parts = []
+ @parts.concat parts
+
+ @file = nil
+ @omit_headings_from_table_of_contents_below = nil
+ end
+
+ ##
+ # Appends +part+ to the document
+
+ def << part
+ case part
+ when RDoc::Markup::Document then
+ unless part.empty? then
+ parts.concat part.parts
+ parts << RDoc::Markup::BlankLine.new
+ end
+ when String then
+ raise ArgumentError,
+ "expected RDoc::Markup::Document and friends, got String" unless
+ part.empty?
+ else
+ parts << part
+ end
+ end
+
+ def == other # :nodoc:
+ self.class == other.class and
+ @file == other.file and
+ @parts == other.parts
+ end
+
+ ##
+ # Runs this document and all its #items through +visitor+
+
+ def accept visitor
+ visitor.start_accepting
+
+ visitor.accept_document self
+
+ visitor.end_accepting
+ end
+
+ ##
+ # Concatenates the given +parts+ onto the document
+
+ def concat parts
+ self.parts.concat parts
+ end
+
+ ##
+ # Enumerator for the parts of this document
+
+ def each &block
+ @parts.each(&block)
+ end
+
+ ##
+ # Does this document have no parts?
+
+ def empty?
+ @parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?)
+ end
+
+ ##
+ # The file this Document was created from.
+
+ def file= location
+ @file = case location
+ when RDoc::TopLevel then
+ location.relative_name
+ else
+ location
+ end
+ end
+
+ ##
+ # When this is a collection of documents (#file is not set and this document
+ # contains only other documents as its direct children) #merge replaces
+ # documents in this class with documents from +other+ when the file matches
+ # and adds documents from +other+ when the files do not.
+ #
+ # The information in +other+ is preferred over the receiver
+
+ def merge other
+ if empty? then
+ @parts = other.parts
+ return self
+ end
+
+ other.parts.each do |other_part|
+ self.parts.delete_if do |self_part|
+ self_part.file and self_part.file == other_part.file
+ end
+
+ self.parts << other_part
+ end
+
+ self
+ end
+
+ ##
+ # Does this Document contain other Documents?
+
+ def merged?
+ RDoc::Markup::Document === @parts.first
+ end
+
+ def pretty_print q # :nodoc:
+ start = @file ? "[doc (#{@file}): " : '[doc: '
+
+ q.group 2, start, ']' do
+ q.seplist @parts do |part|
+ q.pp part
+ end
+ end
+ end
+
+ ##
+ # Appends +parts+ to the document
+
+ def push *parts
+ self.parts.concat parts
+ end
+
+ ##
+ # Returns an Array of headings in the document.
+ #
+ # Require 'rdoc/markup/formatter' before calling this method.
+
+ def table_of_contents
+ accept RDoc::Markup::ToTableOfContents.to_toc
+ end
+
+end
+
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
new file mode 100644
index 0000000000..5dc71d2242
--- /dev/null
+++ b/lib/rdoc/markup/formatter.rb
@@ -0,0 +1,265 @@
+# frozen_string_literal: true
+##
+# Base class for RDoc markup formatters
+#
+# Formatters are a visitor that converts an RDoc::Markup tree (from a comment)
+# into some kind of output. RDoc ships with formatters for converting back to
+# rdoc, ANSI text, HTML, a Table of Contents and other formats.
+#
+# If you'd like to write your own Formatter use
+# RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter
+# use RDoc::Markup::TextFormatterTestCase which provides extra test cases.
+
+class RDoc::Markup::Formatter
+
+ ##
+ # Tag for inline markup containing a +bit+ for the bitmask and the +on+ and
+ # +off+ triggers.
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ ##
+ # Converts a target url to one that is relative to a given path
+
+ def self.gen_relative_url path, target
+ from = File.dirname path
+ to, to_file = File.split target
+
+ from = from.split "/"
+ to = to.split "/"
+
+ from.delete '.'
+ to.delete '.'
+
+ while from.size > 0 and to.size > 0 and from[0] == to[0] do
+ from.shift
+ to.shift
+ end
+
+ from.fill ".."
+ from.concat to
+ from << to_file
+ File.join(*from)
+ end
+
+ ##
+ # Creates a new Formatter
+
+ def initialize options, markup = nil
+ @options = options
+
+ @markup = markup || RDoc::Markup.new
+ @am = @markup.attribute_manager
+ @am.add_special(/<br>/, :HARD_BREAK)
+
+ @attributes = @am.attributes
+
+ @attr_tags = []
+
+ @in_tt = 0
+ @tt_bit = @attributes.bitmap_for :TT
+
+ @hard_break = ''
+ @from_path = '.'
+ end
+
+ ##
+ # Adds +document+ to the output
+
+ def accept_document document
+ document.parts.each do |item|
+ case item
+ when RDoc::Markup::Document then # HACK
+ accept_document item
+ else
+ item.accept self
+ end
+ end
+ end
+
+ ##
+ # Adds a special for links of the form rdoc-...:
+
+ def add_special_RDOCLINK
+ @markup.add_special(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
+ end
+
+ ##
+ # Adds a special for links of the form {<text>}[<url>] and <word>[<url>]
+
+ def add_special_TIDYLINK
+ @markup.add_special(/(?:
+ \{.*?\} | # multi-word label
+ \b[^\s{}]+? # single-word label
+ )
+
+ \[\S+?\] # link target
+ /x, :TIDYLINK)
+ end
+
+ ##
+ # Add a new set of tags for an attribute. We allow separate start and end
+ # tags for flexibility
+
+ def add_tag(name, start, stop)
+ attr = @attributes.bitmap_for name
+ @attr_tags << InlineTag.new(attr, start, stop)
+ end
+
+ ##
+ # Allows +tag+ to be decorated with additional information.
+
+ def annotate(tag)
+ tag
+ end
+
+ ##
+ # Marks up +content+
+
+ def convert content
+ @markup.convert content, self
+ end
+
+ ##
+ # Converts flow items +flow+
+
+ def convert_flow(flow)
+ res = []
+
+ flow.each do |item|
+ case item
+ when String then
+ res << convert_string(item)
+ when RDoc::Markup::AttrChanger then
+ off_tags res, item
+ on_tags res, item
+ when RDoc::Markup::Special then
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+
+ res.join
+ end
+
+ ##
+ # Converts added specials. See RDoc::Markup#add_special
+
+ def convert_special special
+ return special.text if in_tt?
+
+ handled = false
+
+ @attributes.each_name_of special.type do |name|
+ method_name = "handle_special_#{name}"
+
+ if respond_to? method_name then
+ special.text = send method_name, special
+ handled = true
+ end
+ end
+
+ unless handled then
+ special_name = @attributes.as_string special.type
+
+ raise RDoc::Error, "Unhandled special #{special_name}: #{special}"
+ end
+
+ special.text
+ end
+
+ ##
+ # Converts a string to be fancier if desired
+
+ def convert_string string
+ string
+ end
+
+ ##
+ # Use ignore in your subclass to ignore the content of a node.
+ #
+ # ##
+ # # We don't support raw nodes in ToNoRaw
+ #
+ # alias accept_raw ignore
+
+ def ignore *node
+ end
+
+ ##
+ # Are we currently inside tt tags?
+
+ def in_tt?
+ @in_tt > 0
+ end
+
+ ##
+ # Turns on tags for +item+ on +res+
+
+ def on_tags res, item
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0 then
+ res << annotate(tag.on)
+ @in_tt += 1 if tt? tag
+ end
+ end
+ end
+
+ ##
+ # Turns off tags for +item+ on +res+
+
+ def off_tags res, item
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0 then
+ @in_tt -= 1 if tt? tag
+ res << annotate(tag.off)
+ end
+ end
+ end
+
+ ##
+ # Extracts and a scheme, url and an anchor id from +url+ and returns them.
+
+ def parse_url url
+ case url
+ when /^rdoc-label:([^:]*)(?::(.*))?/ then
+ scheme = 'link'
+ path = "##{$1}"
+ id = " id=\"#{$2}\"" if $2
+ when /([A-Za-z]+):(.*)/ then
+ scheme = $1.downcase
+ path = $2
+ when /^#/ then
+ else
+ scheme = 'http'
+ path = url
+ url = url
+ end
+
+ if scheme == 'link' then
+ url = if path[0, 1] == '#' then # is this meaningful?
+ path
+ else
+ self.class.gen_relative_url @from_path, path
+ end
+ end
+
+ [scheme, url, id]
+ end
+
+ ##
+ # Is +tag+ a tt tag?
+
+ def tt? tag
+ tag.bit == @tt_bit
+ end
+
+end
+
diff --git a/lib/rdoc/markup/formatter_test_case.rb b/lib/rdoc/markup/formatter_test_case.rb
new file mode 100644
index 0000000000..076b7c81bb
--- /dev/null
+++ b/lib/rdoc/markup/formatter_test_case.rb
@@ -0,0 +1,764 @@
+# frozen_string_literal: true
+require 'minitest/unit'
+
+##
+# Test case for creating new RDoc::Markup formatters. See
+# test/test_rdoc_markup_to_*.rb for examples.
+#
+# This test case adds a variety of tests to your subclass when
+# #add_visitor_tests is called. Most tests set up a scenario then call a
+# method you will provide to perform the assertion on the output.
+#
+# Your subclass must instantiate a visitor and assign it to <tt>@to</tt>.
+#
+# For example, test_accept_blank_line sets up a RDoc::Markup::BlockLine then
+# calls accept_blank_line on your visitor. You are responsible for asserting
+# that the output is correct.
+#
+# Example:
+#
+# class TestRDocMarkupToNewFormat < RDoc::Markup::FormatterTestCase
+#
+# add_visitor_tests
+#
+# def setup
+# super
+#
+# @to = RDoc::Markup::ToNewFormat.new
+# end
+#
+# def accept_blank_line
+# assert_equal :junk, @to.res.join
+# end
+#
+# # ...
+#
+# end
+
+class RDoc::Markup::FormatterTestCase < RDoc::TestCase
+
+ ##
+ # Call #setup when inheriting from this test case.
+ #
+ # Provides the following instance variables:
+ #
+ # +@m+:: RDoc::Markup.new
+ # +@RM+:: RDoc::Markup # to reduce typing
+ # +@bullet_list+:: @RM::List.new :BULLET, # ...
+ # +@label_list+:: @RM::List.new :LABEL, # ...
+ # +@lalpha_list+:: @RM::List.new :LALPHA, # ...
+ # +@note_list+:: @RM::List.new :NOTE, # ...
+ # +@number_list+:: @RM::List.new :NUMBER, # ...
+ # +@ualpha_list+:: @RM::List.new :UALPHA, # ...
+
+ def setup
+ super
+
+ @options = RDoc::Options.new
+
+ @m = @RM.new
+
+ @bullet_list = @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
+
+ @label_list = @RM::List.new(:LABEL,
+ @RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')),
+ @RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too')))
+
+ @lalpha_list = @RM::List.new(:LALPHA,
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
+
+ @note_list = @RM::List.new(:NOTE,
+ @RM::ListItem.new('cat', @RM::Paragraph.new('cats are cool')),
+ @RM::ListItem.new('dog', @RM::Paragraph.new('dogs are cool too')))
+
+ @number_list = @RM::List.new(:NUMBER,
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
+
+ @ualpha_list = @RM::List.new(:UALPHA,
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
+ @RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
+ end
+
+ ##
+ # Call to add the visitor tests to your test case
+
+ def self.add_visitor_tests
+ class_eval do
+
+ ##
+ # Calls start_accepting which needs to verify startup state
+
+ def test_start_accepting
+ @to.start_accepting
+
+ start_accepting
+ end
+
+ ##
+ # Calls end_accepting on your test case which needs to call
+ # <tt>@to.end_accepting</tt> and verify document generation
+
+ def test_end_accepting
+ @to.start_accepting
+ @to.res << 'hi'
+
+ end_accepting
+ end
+
+ ##
+ # Calls accept_blank_line
+
+ def test_accept_blank_line
+ @to.start_accepting
+
+ @to.accept_blank_line @RM::BlankLine.new
+
+ accept_blank_line
+ end
+
+ ##
+ # Calls accept_block_quote
+
+ def test_accept_block_quote
+ @to.start_accepting
+
+ @to.accept_block_quote block para 'quote'
+
+ accept_block_quote
+ end
+ ##
+ # Test case that calls <tt>@to.accept_document</tt>
+
+ def test_accept_document
+ @to.start_accepting
+ @to.accept_document @RM::Document.new @RM::Paragraph.new 'hello'
+
+ accept_document
+ end
+
+ ##
+ # Calls accept_heading with a level 5 RDoc::Markup::Heading
+
+ def test_accept_heading
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(5, 'Hello')
+
+ accept_heading
+ end
+
+ ##
+ # Calls accept_heading_1 with a level 1 RDoc::Markup::Heading
+
+ def test_accept_heading_1
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ accept_heading_1
+ end
+
+ ##
+ # Calls accept_heading_2 with a level 2 RDoc::Markup::Heading
+
+ def test_accept_heading_2
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(2, 'Hello')
+
+ accept_heading_2
+ end
+
+ ##
+ # Calls accept_heading_3 with a level 3 RDoc::Markup::Heading
+
+ def test_accept_heading_3
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(3, 'Hello')
+
+ accept_heading_3
+ end
+
+ ##
+ # Calls accept_heading_4 with a level 4 RDoc::Markup::Heading
+
+ def test_accept_heading_4
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(4, 'Hello')
+
+ accept_heading_4
+ end
+
+ ##
+ # Calls accept_heading_b with a bold level 1 RDoc::Markup::Heading
+
+ def test_accept_heading_b
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, '*Hello*')
+
+ accept_heading_b
+ end
+
+ ##
+ # Calls accept_heading_suppressed_crossref with a level 1
+ # RDoc::Markup::Heading containing a suppressed crossref
+
+ def test_accept_heading_suppressed_crossref # HACK to_html_crossref test
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, '\\Hello')
+
+ accept_heading_suppressed_crossref
+ end
+
+ ##
+ # Calls accept_paragraph
+
+ def test_accept_paragraph
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('hi')
+
+ accept_paragraph
+ end
+
+ ##
+ # Calls accept_paragraph_b with a RDoc::Markup::Paragraph containing
+ # bold words
+
+ def test_accept_paragraph_b
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg <b>bold words</b> reg')
+
+ accept_paragraph_b
+ end
+
+ ##
+ # Calls accept_paragraph_br with a RDoc::Markup::Paragraph containing
+ # a \<br>
+
+ def test_accept_paragraph_br
+ @to.start_accepting
+
+ @to.accept_paragraph para 'one<br>two'
+
+ accept_paragraph_br
+ end
+
+ ##
+ # Calls accept_paragraph with a Paragraph containing a hard break
+
+ def test_accept_paragraph_break
+ @to.start_accepting
+
+ @to.accept_paragraph para('hello', hard_break, 'world')
+
+ accept_paragraph_break
+ end
+
+ ##
+ # Calls accept_paragraph_i with a RDoc::Markup::Paragraph containing
+ # emphasized words
+
+ def test_accept_paragraph_i
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg <em>italic words</em> reg')
+
+ accept_paragraph_i
+ end
+
+ ##
+ # Calls accept_paragraph_plus with a RDoc::Markup::Paragraph containing
+ # teletype words
+
+ def test_accept_paragraph_plus
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg +teletype+ reg')
+
+ accept_paragraph_plus
+ end
+
+ ##
+ # Calls accept_paragraph_star with a RDoc::Markup::Paragraph containing
+ # bold words
+
+ def test_accept_paragraph_star
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg *bold* reg')
+
+ accept_paragraph_star
+ end
+
+ ##
+ # Calls accept_paragraph_underscore with a RDoc::Markup::Paragraph
+ # containing emphasized words
+
+ def test_accept_paragraph_underscore
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg _italic_ reg')
+
+ accept_paragraph_underscore
+ end
+
+ ##
+ # Calls accept_verbatim with a RDoc::Markup::Verbatim
+
+ def test_accept_verbatim
+ @to.start_accepting
+
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
+
+ accept_verbatim
+ end
+
+ ##
+ # Calls accept_raw with a RDoc::Markup::Raw
+
+ def test_accept_raw
+ @to.start_accepting
+
+ @to.accept_raw @RM::Raw.new("<table>",
+ "<tr><th>Name<th>Count",
+ "<tr><td>a<td>1",
+ "<tr><td>b<td>2",
+ "</table>")
+
+ accept_raw
+ end
+
+ ##
+ # Calls accept_rule with a RDoc::Markup::Rule
+
+ def test_accept_rule
+ @to.start_accepting
+
+ @to.accept_rule @RM::Rule.new(4)
+
+ accept_rule
+ end
+
+ ##
+ # Calls accept_list_item_start_bullet
+
+ def test_accept_list_item_start_bullet
+ @to.start_accepting
+
+ @to.accept_list_start @bullet_list
+
+ @to.accept_list_item_start @bullet_list.items.first
+
+ accept_list_item_start_bullet
+ end
+
+ ##
+ # Calls accept_list_item_start_label
+
+ def test_accept_list_item_start_label
+ @to.start_accepting
+
+ @to.accept_list_start @label_list
+
+ @to.accept_list_item_start @label_list.items.first
+
+ accept_list_item_start_label
+ end
+
+ ##
+ # Calls accept_list_item_start_lalpha
+
+ def test_accept_list_item_start_lalpha
+ @to.start_accepting
+
+ @to.accept_list_start @lalpha_list
+
+ @to.accept_list_item_start @lalpha_list.items.first
+
+ accept_list_item_start_lalpha
+ end
+
+ ##
+ # Calls accept_list_item_start_note
+
+ def test_accept_list_item_start_note
+ @to.start_accepting
+
+ @to.accept_list_start @note_list
+
+ @to.accept_list_item_start @note_list.items.first
+
+ accept_list_item_start_note
+ end
+
+ ##
+ # Calls accept_list_item_start_note_2
+
+ def test_accept_list_item_start_note_2
+ list = list(:NOTE,
+ item('<tt>teletype</tt>',
+ para('teletype description')))
+
+ @to.start_accepting
+
+ list.accept @to
+
+ @to.end_accepting
+
+ accept_list_item_start_note_2
+ end
+
+ ##
+ # Calls accept_list_item_start_note_multi_description
+
+ def test_accept_list_item_start_note_multi_description
+ list = list(:NOTE,
+ item(%w[label],
+ para('description one')),
+ item(nil, para('description two')))
+
+ @to.start_accepting
+
+ list.accept @to
+
+ @to.end_accepting
+
+ accept_list_item_start_note_multi_description
+ end
+
+ ##
+ # Calls accept_list_item_start_note_multi_label
+
+ def test_accept_list_item_start_note_multi_label
+ list = list(:NOTE,
+ item(%w[one two],
+ para('two headers')))
+
+ @to.start_accepting
+
+ list.accept @to
+
+ @to.end_accepting
+
+ accept_list_item_start_note_multi_label
+ end
+
+ ##
+ # Calls accept_list_item_start_number
+
+ def test_accept_list_item_start_number
+ @to.start_accepting
+
+ @to.accept_list_start @number_list
+
+ @to.accept_list_item_start @number_list.items.first
+
+ accept_list_item_start_number
+ end
+
+ ##
+ # Calls accept_list_item_start_ualpha
+
+ def test_accept_list_item_start_ualpha
+ @to.start_accepting
+
+ @to.accept_list_start @ualpha_list
+
+ @to.accept_list_item_start @ualpha_list.items.first
+
+ accept_list_item_start_ualpha
+ end
+
+ ##
+ # Calls accept_list_item_end_bullet
+
+ def test_accept_list_item_end_bullet
+ @to.start_accepting
+
+ @to.accept_list_start @bullet_list
+
+ @to.accept_list_item_start @bullet_list.items.first
+
+ @to.accept_list_item_end @bullet_list.items.first
+
+ accept_list_item_end_bullet
+ end
+
+ ##
+ # Calls accept_list_item_end_label
+
+ def test_accept_list_item_end_label
+ @to.start_accepting
+
+ @to.accept_list_start @label_list
+
+ @to.accept_list_item_start @label_list.items.first
+
+ @to.accept_list_item_end @label_list.items.first
+
+ accept_list_item_end_label
+ end
+
+ ##
+ # Calls accept_list_item_end_lalpha
+
+ def test_accept_list_item_end_lalpha
+ @to.start_accepting
+
+ @to.accept_list_start @lalpha_list
+
+ @to.accept_list_item_start @lalpha_list.items.first
+
+ @to.accept_list_item_end @lalpha_list.items.first
+
+ accept_list_item_end_lalpha
+ end
+
+ ##
+ # Calls accept_list_item_end_note
+
+ def test_accept_list_item_end_note
+ @to.start_accepting
+
+ @to.accept_list_start @note_list
+
+ @to.accept_list_item_start @note_list.items.first
+
+ @to.accept_list_item_end @note_list.items.first
+
+ accept_list_item_end_note
+ end
+
+ ##
+ # Calls accept_list_item_end_number
+
+ def test_accept_list_item_end_number
+ @to.start_accepting
+
+ @to.accept_list_start @number_list
+
+ @to.accept_list_item_start @number_list.items.first
+
+ @to.accept_list_item_end @number_list.items.first
+
+ accept_list_item_end_number
+ end
+
+ ##
+ # Calls accept_list_item_end_ualpha
+
+ def test_accept_list_item_end_ualpha
+ @to.start_accepting
+
+ @to.accept_list_start @ualpha_list
+
+ @to.accept_list_item_start @ualpha_list.items.first
+
+ @to.accept_list_item_end @ualpha_list.items.first
+
+ accept_list_item_end_ualpha
+ end
+
+ ##
+ # Calls accept_list_start_bullet
+
+ def test_accept_list_start_bullet
+ @to.start_accepting
+
+ @to.accept_list_start @bullet_list
+
+ accept_list_start_bullet
+ end
+
+ ##
+ # Calls accept_list_start_label
+
+ def test_accept_list_start_label
+ @to.start_accepting
+
+ @to.accept_list_start @label_list
+
+ accept_list_start_label
+ end
+
+ ##
+ # Calls accept_list_start_lalpha
+
+ def test_accept_list_start_lalpha
+ @to.start_accepting
+
+ @to.accept_list_start @lalpha_list
+
+ accept_list_start_lalpha
+ end
+
+ ##
+ # Calls accept_list_start_note
+
+ def test_accept_list_start_note
+ @to.start_accepting
+
+ @to.accept_list_start @note_list
+
+ accept_list_start_note
+ end
+
+ ##
+ # Calls accept_list_start_number
+
+ def test_accept_list_start_number
+ @to.start_accepting
+
+ @to.accept_list_start @number_list
+
+ accept_list_start_number
+ end
+
+ ##
+ # Calls accept_list_start_ualpha
+
+ def test_accept_list_start_ualpha
+ @to.start_accepting
+
+ @to.accept_list_start @ualpha_list
+
+ accept_list_start_ualpha
+ end
+
+ ##
+ # Calls accept_list_end_bullet
+
+ def test_accept_list_end_bullet
+ @to.start_accepting
+
+ @to.accept_list_start @bullet_list
+
+ @to.accept_list_end @bullet_list
+
+ accept_list_end_bullet
+ end
+
+ ##
+ # Calls accept_list_end_label
+
+ def test_accept_list_end_label
+ @to.start_accepting
+
+ @to.accept_list_start @label_list
+
+ @to.accept_list_end @label_list
+
+ accept_list_end_label
+ end
+
+ ##
+ # Calls accept_list_end_lalpha
+
+ def test_accept_list_end_lalpha
+ @to.start_accepting
+
+ @to.accept_list_start @lalpha_list
+
+ @to.accept_list_end @lalpha_list
+
+ accept_list_end_lalpha
+ end
+
+ ##
+ # Calls accept_list_end_number
+
+ def test_accept_list_end_number
+ @to.start_accepting
+
+ @to.accept_list_start @number_list
+
+ @to.accept_list_end @number_list
+
+ accept_list_end_number
+ end
+
+ ##
+ # Calls accept_list_end_note
+
+ def test_accept_list_end_note
+ @to.start_accepting
+
+ @to.accept_list_start @note_list
+
+ @to.accept_list_end @note_list
+
+ accept_list_end_note
+ end
+
+ ##
+ # Calls accept_list_end_ualpha
+
+ def test_accept_list_end_ualpha
+ @to.start_accepting
+
+ @to.accept_list_start @ualpha_list
+
+ @to.accept_list_end @ualpha_list
+
+ accept_list_end_ualpha
+ end
+
+ ##
+ # Calls list_nested with a two-level list
+
+ def test_list_nested
+ doc = @RM::Document.new(
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'),
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1.1')))),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))))
+
+ doc.accept @to
+
+ list_nested
+ end
+
+ ##
+ # Calls list_verbatim with a list containing a verbatim block
+
+ def test_list_verbatim # HACK overblown
+ doc =
+ doc(
+ list(:BULLET,
+ item(nil,
+ para('list stuff'),
+ blank_line,
+ verb("* list\n",
+ " with\n",
+ "\n",
+ " second\n",
+ "\n",
+ " 1. indented\n",
+ " 2. numbered\n",
+ "\n",
+ " third\n",
+ "\n",
+ "* second\n"))))
+
+ doc.accept @to
+
+ list_verbatim
+ end
+ end
+ end
+
+end
diff --git a/lib/rdoc/markup/hard_break.rb b/lib/rdoc/markup/hard_break.rb
new file mode 100644
index 0000000000..046068d5c2
--- /dev/null
+++ b/lib/rdoc/markup/hard_break.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+##
+# A hard-break in the middle of a paragraph.
+
+class RDoc::Markup::HardBreak
+
+ @instance = new
+
+ ##
+ # RDoc::Markup::HardBreak is a singleton
+
+ def self.new
+ @instance
+ end
+
+ ##
+ # Calls #accept_hard_break on +visitor+
+
+ def accept visitor
+ visitor.accept_hard_break self
+ end
+
+ def == other # :nodoc:
+ self.class === other
+ end
+
+ def pretty_print q # :nodoc:
+ q.text "[break]"
+ end
+
+end
+
diff --git a/lib/rdoc/markup/heading.rb b/lib/rdoc/markup/heading.rb
new file mode 100644
index 0000000000..233774c5c4
--- /dev/null
+++ b/lib/rdoc/markup/heading.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+##
+# A heading with a level (1-6) and text
+
+RDoc::Markup::Heading =
+ Struct.new :level, :text do
+
+ @to_html = nil
+ @to_label = nil
+
+ ##
+ # A singleton RDoc::Markup::ToLabel formatter for headings.
+
+ def self.to_label
+ @to_label ||= RDoc::Markup::ToLabel.new
+ end
+
+ ##
+ # A singleton plain HTML formatter for headings. Used for creating labels
+ # for the Table of Contents
+
+ def self.to_html
+ return @to_html if @to_html
+
+ markup = RDoc::Markup.new
+ markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+
+ @to_html = RDoc::Markup::ToHtml.new nil
+
+ def @to_html.handle_special_CROSSREF special
+ special.text.sub(/^\\/, '')
+ end
+
+ @to_html
+ end
+
+ ##
+ # Calls #accept_heading on +visitor+
+
+ def accept visitor
+ visitor.accept_heading self
+ end
+
+ ##
+ # An HTML-safe anchor reference for this header.
+
+ def aref
+ "label-#{self.class.to_label.convert text.dup}"
+ end
+
+ ##
+ # Creates a fully-qualified label which will include the label from
+ # +context+. This helps keep ids unique in HTML.
+
+ def label context = nil
+ label = aref
+
+ label = [context.aref, label].compact.join '-' if
+ context and context.respond_to? :aref
+
+ label
+ end
+
+ ##
+ # HTML markup of the text of this label without the surrounding header
+ # element.
+
+ def plain_html
+ self.class.to_html.to_html(text.dup)
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[head: #{level} ", ']' do
+ q.pp text
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/include.rb b/lib/rdoc/markup/include.rb
new file mode 100644
index 0000000000..ad7c4a9640
--- /dev/null
+++ b/lib/rdoc/markup/include.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+##
+# A file included at generation time. Objects of this class are created by
+# RDoc::RD for an extension-less include.
+#
+# This implementation in incomplete.
+
+class RDoc::Markup::Include
+
+ ##
+ # The filename to be included, without extension
+
+ attr_reader :file
+
+ ##
+ # Directories to search for #file
+
+ attr_reader :include_path
+
+ ##
+ # Creates a new include that will import +file+ from +include_path+
+
+ def initialize file, include_path
+ @file = file
+ @include_path = include_path
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @file == other.file and @include_path == other.include_path
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[incl ', ']' do
+ q.text file
+ q.breakable
+ q.text 'from '
+ q.pp include_path
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/indented_paragraph.rb b/lib/rdoc/markup/indented_paragraph.rb
new file mode 100644
index 0000000000..d42b2e52b8
--- /dev/null
+++ b/lib/rdoc/markup/indented_paragraph.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+##
+# An Indented Paragraph of text
+
+class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
+
+ ##
+ # The indent in number of spaces
+
+ attr_reader :indent
+
+ ##
+ # Creates a new IndentedParagraph containing +parts+ indented with +indent+
+ # spaces
+
+ def initialize indent, *parts
+ @indent = indent
+
+ super(*parts)
+ end
+
+ def == other # :nodoc:
+ super and indent == other.indent
+ end
+
+ ##
+ # Calls #accept_indented_paragraph on +visitor+
+
+ def accept visitor
+ visitor.accept_indented_paragraph self
+ end
+
+ ##
+ # Joins the raw paragraph text and converts inline HardBreaks to the
+ # +hard_break+ text followed by the indent.
+
+ def text hard_break = nil
+ @parts.map do |part|
+ if RDoc::Markup::HardBreak === part then
+ '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break
+ else
+ part
+ end
+ end.join
+ end
+
+end
+
diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb
new file mode 100644
index 0000000000..aba7ec21ce
--- /dev/null
+++ b/lib/rdoc/markup/inline.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: true
+warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w
diff --git a/lib/rdoc/markup/list.rb b/lib/rdoc/markup/list.rb
new file mode 100644
index 0000000000..05c3609202
--- /dev/null
+++ b/lib/rdoc/markup/list.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+##
+# A List is a homogeneous set of ListItems.
+#
+# The supported list types include:
+#
+# :BULLET::
+# An unordered list
+# :LABEL::
+# An unordered definition list, but using an alternate RDoc::Markup syntax
+# :LALPHA::
+# An ordered list using increasing lowercase English letters
+# :NOTE::
+# An unordered definition list
+# :NUMBER::
+# An ordered list using increasing Arabic numerals
+# :UALPHA::
+# An ordered list using increasing uppercase English letters
+#
+# Definition lists behave like HTML definition lists. Each list item can
+# describe multiple terms. See RDoc::Markup::ListItem for how labels and
+# definition are stored as list items.
+
+class RDoc::Markup::List
+
+ ##
+ # The list's type
+
+ attr_accessor :type
+
+ ##
+ # Items in the list
+
+ attr_reader :items
+
+ ##
+ # Creates a new list of +type+ with +items+. Valid list types are:
+ # +:BULLET+, +:LABEL+, +:LALPHA+, +:NOTE+, +:NUMBER+, +:UALPHA+
+
+ def initialize type = nil, *items
+ @type = type
+ @items = []
+ @items.concat items
+ end
+
+ ##
+ # Appends +item+ to the list
+
+ def << item
+ @items << item
+ end
+
+ def == other # :nodoc:
+ self.class == other.class and
+ @type == other.type and
+ @items == other.items
+ end
+
+ ##
+ # Runs this list and all its #items through +visitor+
+
+ def accept visitor
+ visitor.accept_list_start self
+
+ @items.each do |item|
+ item.accept visitor
+ end
+
+ visitor.accept_list_end self
+ end
+
+ ##
+ # Is the list empty?
+
+ def empty?
+ @items.empty?
+ end
+
+ ##
+ # Returns the last item in the list
+
+ def last
+ @items.last
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[list: #{@type} ", ']' do
+ q.seplist @items do |item|
+ q.pp item
+ end
+ end
+ end
+
+ ##
+ # Appends +items+ to the list
+
+ def push *items
+ @items.concat items
+ end
+
+end
+
diff --git a/lib/rdoc/markup/list_item.rb b/lib/rdoc/markup/list_item.rb
new file mode 100644
index 0000000000..d22554ee73
--- /dev/null
+++ b/lib/rdoc/markup/list_item.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+##
+# An item within a List that contains paragraphs, headings, etc.
+#
+# For BULLET, NUMBER, LALPHA and UALPHA lists, the label will always be nil.
+# For NOTE and LABEL lists, the list label may contain:
+#
+# * a single String for a single label
+# * an Array of Strings for a list item with multiple terms
+# * nil for an extra description attached to a previously labeled list item
+
+class RDoc::Markup::ListItem
+
+ ##
+ # The label for the ListItem
+
+ attr_accessor :label
+
+ ##
+ # Parts of the ListItem
+
+ attr_reader :parts
+
+ ##
+ # Creates a new ListItem with an optional +label+ containing +parts+
+
+ def initialize label = nil, *parts
+ @label = label
+ @parts = []
+ @parts.concat parts
+ end
+
+ ##
+ # Appends +part+ to the ListItem
+
+ def << part
+ @parts << part
+ end
+
+ def == other # :nodoc:
+ self.class == other.class and
+ @label == other.label and
+ @parts == other.parts
+ end
+
+ ##
+ # Runs this list item and all its #parts through +visitor+
+
+ def accept visitor
+ visitor.accept_list_item_start self
+
+ @parts.each do |part|
+ part.accept visitor
+ end
+
+ visitor.accept_list_item_end self
+ end
+
+ ##
+ # Is the ListItem empty?
+
+ def empty?
+ @parts.empty?
+ end
+
+ ##
+ # Length of parts in the ListItem
+
+ def length
+ @parts.length
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[item: ', ']' do
+ case @label
+ when Array then
+ q.pp @label
+ q.text ';'
+ q.breakable
+ when String then
+ q.pp @label
+ q.text ';'
+ q.breakable
+ end
+
+ q.seplist @parts do |part|
+ q.pp part
+ end
+ end
+ end
+
+ ##
+ # Adds +parts+ to the ListItem
+
+ def push *parts
+ @parts.concat parts
+ end
+
+end
+
diff --git a/lib/rdoc/markup/paragraph.rb b/lib/rdoc/markup/paragraph.rb
new file mode 100644
index 0000000000..a2e45ef009
--- /dev/null
+++ b/lib/rdoc/markup/paragraph.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+##
+# A Paragraph of text
+
+class RDoc::Markup::Paragraph < RDoc::Markup::Raw
+
+ ##
+ # Calls #accept_paragraph on +visitor+
+
+ def accept visitor
+ visitor.accept_paragraph self
+ end
+
+ ##
+ # Joins the raw paragraph text and converts inline HardBreaks to the
+ # +hard_break+ text.
+
+ def text hard_break = ''
+ @parts.map do |part|
+ if RDoc::Markup::HardBreak === part then
+ hard_break
+ else
+ part
+ end
+ end.join
+ end
+
+end
+
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
new file mode 100644
index 0000000000..f08587e676
--- /dev/null
+++ b/lib/rdoc/markup/parser.rb
@@ -0,0 +1,543 @@
+# frozen_string_literal: true
+require 'strscan'
+
+##
+# A recursive-descent parser for RDoc markup.
+#
+# The parser tokenizes an input string then parses the tokens into a Document.
+# Documents can be converted into output formats by writing a visitor like
+# RDoc::Markup::ToHTML.
+#
+# The parser only handles the block-level constructs Paragraph, List,
+# ListItem, Heading, Verbatim, BlankLine and Rule. Inline markup such as
+# <tt>\+blah\+</tt> is handled separately by RDoc::Markup::AttributeManager.
+#
+# To see what markup the Parser implements read RDoc. To see how to use
+# RDoc markup to format text in your program read RDoc::Markup.
+
+class RDoc::Markup::Parser
+
+ include RDoc::Text
+
+ ##
+ # List token types
+
+ LIST_TOKENS = [
+ :BULLET,
+ :LABEL,
+ :LALPHA,
+ :NOTE,
+ :NUMBER,
+ :UALPHA,
+ ]
+
+ ##
+ # Parser error subclass
+
+ class Error < RuntimeError; end
+
+ ##
+ # Raised when the parser is unable to handle the given markup
+
+ class ParseError < Error; end
+
+ ##
+ # Enables display of debugging information
+
+ attr_accessor :debug
+
+ ##
+ # Token accessor
+
+ attr_reader :tokens
+
+ ##
+ # Parses +str+ into a Document.
+ #
+ # Use RDoc::Markup#parse instead of this method.
+
+ def self.parse str
+ parser = new
+ parser.tokenize str
+ doc = RDoc::Markup::Document.new
+ parser.parse doc
+ end
+
+ ##
+ # Returns a token stream for +str+, for testing
+
+ def self.tokenize str
+ parser = new
+ parser.tokenize str
+ parser.tokens
+ end
+
+ ##
+ # Creates a new Parser. See also ::parse
+
+ def initialize
+ @binary_input = nil
+ @current_token = nil
+ @debug = false
+ @input = nil
+ @input_encoding = nil
+ @line = 0
+ @line_pos = 0
+ @s = nil
+ @tokens = []
+ end
+
+ ##
+ # Builds a Heading of +level+
+
+ def build_heading level
+ type, text, = get
+
+ text = case type
+ when :TEXT then
+ skip :NEWLINE
+ text
+ else
+ unget
+ ''
+ end
+
+ RDoc::Markup::Heading.new level, text
+ end
+
+ ##
+ # Builds a List flush to +margin+
+
+ def build_list margin
+ p :list_start => margin if @debug
+
+ list = RDoc::Markup::List.new
+ label = nil
+
+ until @tokens.empty? do
+ type, data, column, = get
+
+ case type
+ when *LIST_TOKENS then
+ if column < margin || (list.type && list.type != type) then
+ unget
+ break
+ end
+
+ list.type = type
+ peek_type, _, column, = peek_token
+
+ case type
+ when :NOTE, :LABEL then
+ label = [] unless label
+
+ if peek_type == :NEWLINE then
+ # description not on the same line as LABEL/NOTE
+ # skip the trailing newline & any blank lines below
+ while peek_type == :NEWLINE
+ get
+ peek_type, _, column, = peek_token
+ end
+
+ # we may be:
+ # - at end of stream
+ # - at a column < margin:
+ # [text]
+ # blah blah blah
+ # - at the same column, but with a different type of list item
+ # [text]
+ # * blah blah
+ # - at the same column, with the same type of list item
+ # [one]
+ # [two]
+ # In all cases, we have an empty description.
+ # In the last case only, we continue.
+ if peek_type.nil? || column < margin then
+ empty = true
+ elsif column == margin then
+ case peek_type
+ when type
+ empty = :continue
+ when *LIST_TOKENS
+ empty = true
+ else
+ empty = false
+ end
+ else
+ empty = false
+ end
+
+ if empty then
+ label << data
+ next if empty == :continue
+ break
+ end
+ end
+ else
+ data = nil
+ end
+
+ if label then
+ data = label << data
+ label = nil
+ end
+
+ list_item = RDoc::Markup::ListItem.new data
+ parse list_item, column
+ list << list_item
+
+ else
+ unget
+ break
+ end
+ end
+
+ p :list_end => margin if @debug
+
+ if list.empty? then
+ return nil unless label
+ return nil unless [:LABEL, :NOTE].include? list.type
+
+ list_item = RDoc::Markup::ListItem.new label, RDoc::Markup::BlankLine.new
+ list << list_item
+ end
+
+ list
+ end
+
+ ##
+ # Builds a Paragraph that is flush to +margin+
+
+ def build_paragraph margin
+ p :paragraph_start => margin if @debug
+
+ paragraph = RDoc::Markup::Paragraph.new
+
+ until @tokens.empty? do
+ type, data, column, = get
+
+ if type == :TEXT and column == margin then
+ paragraph << data
+
+ break if peek_token.first == :BREAK
+
+ data << ' ' if skip :NEWLINE
+ else
+ unget
+ break
+ end
+ end
+
+ paragraph.parts.last.sub!(/ \z/, '') # cleanup
+
+ p :paragraph_end => margin if @debug
+
+ paragraph
+ end
+
+ ##
+ # Builds a Verbatim that is indented from +margin+.
+ #
+ # The verbatim block is shifted left (the least indented lines start in
+ # column 0). Each part of the verbatim is one line of text, always
+ # terminated by a newline. Blank lines always consist of a single newline
+ # character, and there is never a single newline at the end of the verbatim.
+
+ def build_verbatim margin
+ p :verbatim_begin => margin if @debug
+ verbatim = RDoc::Markup::Verbatim.new
+
+ min_indent = nil
+ generate_leading_spaces = true
+ line = ''.dup
+
+ until @tokens.empty? do
+ type, data, column, = get
+
+ if type == :NEWLINE then
+ line << data
+ verbatim << line
+ line = ''.dup
+ generate_leading_spaces = true
+ next
+ end
+
+ if column <= margin
+ unget
+ break
+ end
+
+ if generate_leading_spaces then
+ indent = column - margin
+ line << ' ' * indent
+ min_indent = indent if min_indent.nil? || indent < min_indent
+ generate_leading_spaces = false
+ end
+
+ case type
+ when :HEADER then
+ line << '=' * data
+ _, _, peek_column, = peek_token
+ peek_column ||= column + data
+ indent = peek_column - column - data
+ line << ' ' * indent
+ when :RULE then
+ width = 2 + data
+ line << '-' * width
+ _, _, peek_column, = peek_token
+ peek_column ||= column + width
+ indent = peek_column - column - width
+ line << ' ' * indent
+ when :BREAK, :TEXT then
+ line << data
+ else # *LIST_TOKENS
+ list_marker = case type
+ when :BULLET then data
+ when :LABEL then "[#{data}]"
+ when :NOTE then "#{data}::"
+ else # :LALPHA, :NUMBER, :UALPHA
+ "#{data}."
+ end
+ line << list_marker
+ peek_type, _, peek_column = peek_token
+ unless peek_type == :NEWLINE then
+ peek_column ||= column + list_marker.length
+ indent = peek_column - column - list_marker.length
+ line << ' ' * indent
+ end
+ end
+
+ end
+
+ verbatim << line << "\n" unless line.empty?
+ verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0
+ verbatim.normalize
+
+ p :verbatim_end => margin if @debug
+
+ verbatim
+ end
+
+ ##
+ # The character offset for the input string at the given +byte_offset+
+
+ def char_pos byte_offset
+ @input.byteslice(0, byte_offset).length
+ end
+
+ ##
+ # Pulls the next token from the stream.
+
+ def get
+ @current_token = @tokens.shift
+ p :get => @current_token if @debug
+ @current_token
+ end
+
+ ##
+ # Parses the tokens into an array of RDoc::Markup::XXX objects,
+ # and appends them to the passed +parent+ RDoc::Markup::YYY object.
+ #
+ # Exits at the end of the token stream, or when it encounters a token
+ # in a column less than +indent+ (unless it is a NEWLINE).
+ #
+ # Returns +parent+.
+
+ def parse parent, indent = 0
+ p :parse_start => indent if @debug
+
+ until @tokens.empty? do
+ type, data, column, = get
+
+ case type
+ when :BREAK then
+ parent << RDoc::Markup::BlankLine.new
+ skip :NEWLINE, false
+ next
+ when :NEWLINE then
+ # trailing newlines are skipped below, so this is a blank line
+ parent << RDoc::Markup::BlankLine.new
+ skip :NEWLINE, false
+ next
+ end
+
+ # indentation change: break or verbatim
+ if column < indent then
+ unget
+ break
+ elsif column > indent then
+ unget
+ parent << build_verbatim(indent)
+ next
+ end
+
+ # indentation is the same
+ case type
+ when :HEADER then
+ parent << build_heading(data)
+ when :RULE then
+ parent << RDoc::Markup::Rule.new(data)
+ skip :NEWLINE
+ when :TEXT then
+ unget
+ parse_text parent, indent
+ when *LIST_TOKENS then
+ unget
+ parent << build_list(indent)
+ else
+ type, data, column, line = @current_token
+ raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
+ end
+ end
+
+ p :parse_end => indent if @debug
+
+ parent
+
+ end
+
+ ##
+ # Small hook that is overridden by RDoc::TomDoc
+
+ def parse_text parent, indent # :nodoc:
+ parent << build_paragraph(indent)
+ end
+
+ ##
+ # Returns the next token on the stream without modifying the stream
+
+ def peek_token
+ token = @tokens.first || []
+ p :peek => token if @debug
+ token
+ end
+
+ ##
+ # Creates the StringScanner
+
+ def setup_scanner input
+ @line = 0
+ @line_pos = 0
+ @input = input.dup
+
+ @s = StringScanner.new input
+ end
+
+ ##
+ # Skips the next token if its type is +token_type+.
+ #
+ # Optionally raises an error if the next token is not of the expected type.
+
+ def skip token_type, error = true
+ type, = get
+ return unless type # end of stream
+ return @current_token if token_type == type
+ unget
+ raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
+ end
+
+ ##
+ # Turns text +input+ into a stream of tokens
+
+ def tokenize input
+ setup_scanner input
+
+ until @s.eos? do
+ pos = @s.pos
+
+ # leading spaces will be reflected by the column of the next token
+ # the only thing we loose are trailing spaces at the end of the file
+ next if @s.scan(/ +/)
+
+ # note: after BULLET, LABEL, etc.,
+ # indent will be the column of the next non-newline token
+
+ @tokens << case
+ # [CR]LF => :NEWLINE
+ when @s.scan(/\r?\n/) then
+ token = [:NEWLINE, @s.matched, *token_pos(pos)]
+ @line_pos = char_pos @s.pos
+ @line += 1
+ token
+ # === text => :HEADER then :TEXT
+ when @s.scan(/(=+)(\s*)/) then
+ level = @s[1].length
+ header = [:HEADER, level, *token_pos(pos)]
+
+ if @s[2] =~ /^\r?\n/ then
+ @s.pos -= @s[2].length
+ header
+ else
+ pos = @s.pos
+ @s.scan(/.*/)
+ @tokens << header
+ [:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)]
+ end
+ # --- (at least 3) and nothing else on the line => :RULE
+ when @s.scan(/(-{3,}) *\r?$/) then
+ [:RULE, @s[1].length - 2, *token_pos(pos)]
+ # * or - followed by white space and text => :BULLET
+ when @s.scan(/([*-]) +(\S)/) then
+ @s.pos -= @s[2].bytesize # unget \S
+ [:BULLET, @s[1], *token_pos(pos)]
+ # A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER
+ when @s.scan(/([a-z]|\d+)\. +(\S)/i) then
+ # FIXME if tab(s), the column will be wrong
+ # either support tabs everywhere by first expanding them to
+ # spaces, or assume that they will have been replaced
+ # before (and provide a check for that at least in debug
+ # mode)
+ list_label = @s[1]
+ @s.pos -= @s[2].bytesize # unget \S
+ list_type =
+ case list_label
+ when /[a-z]/ then :LALPHA
+ when /[A-Z]/ then :UALPHA
+ when /\d/ then :NUMBER
+ else
+ raise ParseError, "BUG token #{list_label}"
+ end
+ [list_type, list_label, *token_pos(pos)]
+ # [text] followed by spaces or end of line => :LABEL
+ when @s.scan(/\[(.*?)\]( +|\r?$)/) then
+ [:LABEL, @s[1], *token_pos(pos)]
+ # text:: followed by spaces or end of line => :NOTE
+ when @s.scan(/(.*?)::( +|\r?$)/) then
+ [:NOTE, @s[1], *token_pos(pos)]
+ # anything else: :TEXT
+ else @s.scan(/(.*?)( )?\r?$/)
+ token = [:TEXT, @s[1], *token_pos(pos)]
+
+ if @s[2] then
+ @tokens << token
+ [:BREAK, @s[2], *token_pos(pos + @s[1].length)]
+ else
+ token
+ end
+ end
+ end
+
+ self
+ end
+
+ ##
+ # Calculates the column (by character) and line of the current token based
+ # on +byte_offset+.
+
+ def token_pos byte_offset
+ offset = char_pos byte_offset
+
+ [offset - @line_pos, @line]
+ end
+
+ ##
+ # Returns the current token to the token stream
+
+ def unget
+ token = @current_token
+ p :unget => token if @debug
+ raise Error, 'too many #ungets' if token == @tokens.first
+ @tokens.unshift token if token
+ end
+
+end
diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb
new file mode 100644
index 0000000000..0ac7a41934
--- /dev/null
+++ b/lib/rdoc/markup/pre_process.rb
@@ -0,0 +1,295 @@
+# frozen_string_literal: true
+##
+# Handle common directives that can occur in a block of text:
+#
+# \:include: filename
+#
+# Directives can be escaped by preceding them with a backslash.
+#
+# RDoc plugin authors can register additional directives to be handled by
+# using RDoc::Markup::PreProcess::register.
+#
+# Any directive that is not built-in to RDoc (including those registered via
+# plugins) will be stored in the metadata hash on the CodeObject the comment
+# is attached to. See RDoc::Markup@Directives for the list of built-in
+# directives.
+
+class RDoc::Markup::PreProcess
+
+ ##
+ # An RDoc::Options instance that will be filled in with overrides from
+ # directives
+
+ attr_accessor :options
+
+ ##
+ # Adds a post-process handler for directives. The handler will be called
+ # with the result RDoc::Comment (or text String) and the code object for the
+ # comment (if any).
+
+ def self.post_process &block
+ @post_processors << block
+ end
+
+ ##
+ # Registered post-processors
+
+ def self.post_processors
+ @post_processors
+ end
+
+ ##
+ # Registers +directive+ as one handled by RDoc. If a block is given the
+ # directive will be replaced by the result of the block, otherwise the
+ # directive will be removed from the processed text.
+ #
+ # The block will be called with the directive name and the directive
+ # parameter:
+ #
+ # RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|
+ # # replace text, etc.
+ # end
+
+ def self.register directive, &block
+ @registered[directive] = block
+ end
+
+ ##
+ # Registered directives
+
+ def self.registered
+ @registered
+ end
+
+ ##
+ # Clears all registered directives and post-processors
+
+ def self.reset
+ @post_processors = []
+ @registered = {}
+ end
+
+ reset
+
+ ##
+ # Creates a new pre-processor for +input_file_name+ that will look for
+ # included files in +include_path+
+
+ def initialize(input_file_name, include_path)
+ @input_file_name = input_file_name
+ @include_path = include_path
+ @options = nil
+ end
+
+ ##
+ # Look for directives in the given +text+.
+ #
+ # Options that we don't handle are yielded. If the block returns false the
+ # directive is restored to the text. If the block returns nil or no block
+ # was given the directive is handled according to the registered directives.
+ # If a String was returned the directive is replaced with the string.
+ #
+ # If no matching directive was registered the directive is restored to the
+ # text.
+ #
+ # If +code_object+ is given and the directive is unknown then the
+ # directive's parameter is set as metadata on the +code_object+. See
+ # RDoc::CodeObject#metadata for details.
+
+ def handle text, code_object = nil, &block
+ if RDoc::Comment === text then
+ comment = text
+ text = text.text
+ end
+
+ # regexp helper (square brackets for optional)
+ # $1 $2 $3 $4 $5
+ # [prefix][\]:directive:[spaces][param]newline
+ text = text.gsub(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?(\r?\n|$)/) do
+ # skip something like ':toto::'
+ next $& if $4.empty? and $5 and $5[0, 1] == ':'
+
+ # skip if escaped
+ next "#$1:#$3:#$4#$5\n" unless $2.empty?
+
+ # This is not in handle_directive because I didn't want to pass another
+ # argument into it
+ if comment and $3 == 'markup' then
+ next "#{$1.strip}\n" unless $5
+ comment.format = $5.downcase
+ next "#{$1.strip}\n"
+ end
+
+ handle_directive $1, $3, $5, code_object, text.encoding, &block
+ end
+
+ if comment then
+ comment.text = text
+ else
+ comment = text
+ end
+
+ self.class.post_processors.each do |handler|
+ handler.call comment, code_object
+ end
+
+ text
+ end
+
+ ##
+ # Performs the actions described by +directive+ and its parameter +param+.
+ #
+ # +code_object+ is used for directives that operate on a class or module.
+ # +prefix+ is used to ensure the replacement for handled directives is
+ # correct. +encoding+ is used for the <tt>include</tt> directive.
+ #
+ # For a list of directives in RDoc see RDoc::Markup.
+ #--
+ # When 1.8.7 support is ditched prefix can be defaulted to ''
+
+ def handle_directive prefix, directive, param, code_object = nil,
+ encoding = nil
+ blankline = "#{prefix.strip}\n"
+ directive = directive.downcase
+
+ case directive
+ when 'arg', 'args' then
+ return "#{prefix}:#{directive}: #{param}\n" unless code_object && code_object.kind_of?(RDoc::AnyMethod)
+
+ code_object.params = param
+
+ blankline
+ when 'category' then
+ if RDoc::Context === code_object then
+ section = code_object.add_section param
+ code_object.temporary_section = section
+ end
+
+ blankline # ignore category if we're not on an RDoc::Context
+ when 'doc' then
+ return blankline unless code_object
+ code_object.document_self = true
+ code_object.force_documentation = true
+
+ blankline
+ when 'enddoc' then
+ return blankline unless code_object
+ code_object.done_documenting = true
+
+ blankline
+ when 'include' then
+ filename = param.split.first
+ include_file filename, prefix, encoding
+ when 'main' then
+ @options.main_page = param if @options.respond_to? :main_page
+
+ blankline
+ when 'nodoc' then
+ return blankline unless code_object
+ code_object.document_self = nil # notify nodoc
+ code_object.document_children = param !~ /all/i
+
+ blankline
+ when 'notnew', 'not_new', 'not-new' then
+ return blankline unless RDoc::AnyMethod === code_object
+
+ code_object.dont_rename_initialize = true
+
+ blankline
+ when 'startdoc' then
+ return blankline unless code_object
+
+ code_object.start_doc
+ code_object.force_documentation = true
+
+ blankline
+ when 'stopdoc' then
+ return blankline unless code_object
+
+ code_object.stop_doc
+
+ blankline
+ when 'title' then
+ @options.default_title = param if @options.respond_to? :default_title=
+
+ blankline
+ when 'yield', 'yields' then
+ return blankline unless code_object
+ # remove parameter &block
+ code_object.params = code_object.params.sub(/,?\s*&\w+/, '') if code_object.params
+
+ code_object.block_params = param
+
+ blankline
+ else
+ result = yield directive, param if block_given?
+
+ case result
+ when nil then
+ code_object.metadata[directive] = param if code_object
+
+ if RDoc::Markup::PreProcess.registered.include? directive then
+ handler = RDoc::Markup::PreProcess.registered[directive]
+ result = handler.call directive, param if handler
+ else
+ result = "#{prefix}:#{directive}: #{param}\n"
+ end
+ when false then
+ result = "#{prefix}:#{directive}: #{param}\n"
+ end
+
+ result
+ end
+ end
+
+ ##
+ # Handles the <tt>:include: _filename_</tt> directive.
+ #
+ # If the first line of the included file starts with '#', and contains
+ # an encoding information in the form 'coding:' or 'coding=', it is
+ # removed.
+ #
+ # If all lines in the included file start with a '#', this leading '#'
+ # is removed before inclusion. The included content is indented like
+ # the <tt>:include:</tt> directive.
+ #--
+ # so all content will be verbatim because of the likely space after '#'?
+ # TODO shift left the whole file content in that case
+ # TODO comment stop/start #-- and #++ in included file must be processed here
+
+ def include_file name, indent, encoding
+ full_name = find_include_file name
+
+ unless full_name then
+ warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
+ return ''
+ end
+
+ content = RDoc::Encoding.read_file full_name, encoding, true
+
+ # strip magic comment
+ content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
+
+ # strip leading '#'s, but only if all lines start with them
+ if content =~ /^[^#]/ then
+ content.gsub(/^/, indent)
+ else
+ content.gsub(/^#?/, indent)
+ 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
diff --git a/lib/rdoc/markup/raw.rb b/lib/rdoc/markup/raw.rb
new file mode 100644
index 0000000000..85e2c8b825
--- /dev/null
+++ b/lib/rdoc/markup/raw.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+##
+# A section of text that is added to the output document as-is
+
+class RDoc::Markup::Raw
+
+ ##
+ # The component parts of the list
+
+ attr_reader :parts
+
+ ##
+ # Creates a new Raw containing +parts+
+
+ def initialize *parts
+ @parts = []
+ @parts.concat parts
+ end
+
+ ##
+ # Appends +text+
+
+ def << text
+ @parts << text
+ end
+
+ def == other # :nodoc:
+ self.class == other.class and @parts == other.parts
+ end
+
+ ##
+ # Calls #accept_raw+ on +visitor+
+
+ def accept visitor
+ visitor.accept_raw self
+ end
+
+ ##
+ # Appends +other+'s parts
+
+ def merge other
+ @parts.concat other.parts
+ end
+
+ def pretty_print q # :nodoc:
+ self.class.name =~ /.*::(\w{1,4})/i
+
+ q.group 2, "[#{$1.downcase}: ", ']' do
+ q.seplist @parts do |part|
+ q.pp part
+ end
+ end
+ end
+
+ ##
+ # Appends +texts+ onto this Paragraph
+
+ def push *texts
+ self.parts.concat texts
+ end
+
+ ##
+ # The raw text
+
+ def text
+ @parts.join ' '
+ end
+
+end
+
diff --git a/lib/rdoc/markup/rule.rb b/lib/rdoc/markup/rule.rb
new file mode 100644
index 0000000000..38c1dc7f56
--- /dev/null
+++ b/lib/rdoc/markup/rule.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+##
+# A horizontal rule with a weight
+
+class RDoc::Markup::Rule < Struct.new :weight
+
+ ##
+ # Calls #accept_rule on +visitor+
+
+ def accept visitor
+ visitor.accept_rule self
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[rule:', ']' do
+ q.pp weight
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/sample/rdoc2latex.rb b/lib/rdoc/markup/sample/rdoc2latex.rb
deleted file mode 100644
index 26563b75da..0000000000
--- a/lib/rdoc/markup/sample/rdoc2latex.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/local/bin/ruby
-# Illustration of a script to convert an RDoc-style file to a LaTeX
-# document
-
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_latex'
-
-p = SM::SimpleMarkup.new
-h = SM::ToLaTeX.new
-
-#puts "\\documentclass{report}"
-#puts "\\usepackage{tabularx}"
-#puts "\\usepackage{parskip}"
-#puts "\\begin{document}"
-puts p.convert(ARGF.read, h)
-#puts "\\end{document}"
diff --git a/lib/rdoc/markup/sample/sample.rb b/lib/rdoc/markup/sample/sample.rb
deleted file mode 100644
index a375b54564..0000000000
--- a/lib/rdoc/markup/sample/sample.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# This program illustrates the basic use of the SimpleMarkup
-# class. It extracts the first comment block from the
-# simple_markup.rb file and converts it into HTML on
-# standard output. Run it using
-#
-# % ruby sample.rb
-#
-# You should be in the sample/ directory when you do this,
-# as it hardwires the path to the files it needs to require.
-# This isn't necessary in the code you write once you've
-# installed the package.
-#
-# For a better way of formatting code comment blocks (and more)
-# see the rdoc package.
-#
-
-$:.unshift "../../.."
-
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_html'
-
-# Extract the comment block from the source file
-
-input_string = ""
-
-File.foreach("../simple_markup.rb") do |line|
- break unless line.gsub!(/^\# ?/, '')
- input_string << line
-end
-
-# Create a markup object
-markup = SM::SimpleMarkup.new
-
-# Attach it to an HTML formatter
-h = SM::ToHtml.new
-
-# And convert out comment block to html. Wrap it a body
-# tag pair to let browsers view it
-
-puts "<html><body>"
-puts markup.convert(input_string, h)
-puts "</body></html>"
diff --git a/lib/rdoc/markup/simple_markup.rb b/lib/rdoc/markup/simple_markup.rb
deleted file mode 100644
index 8193ca02d4..0000000000
--- a/lib/rdoc/markup/simple_markup.rb
+++ /dev/null
@@ -1,476 +0,0 @@
-# = Introduction
-#
-# SimpleMarkup parses plain text documents and attempts to decompose
-# them into their constituent parts. Some of these parts are high-level:
-# paragraphs, chunks of verbatim text, list entries and the like. Other
-# parts happen at the character level: a piece of bold text, a word in
-# code font. This markup is similar in spirit to that used on WikiWiki
-# webs, where folks create web pages using a simple set of formatting
-# rules.
-#
-# SimpleMarkup itself does no output formatting: this is left to a
-# different set of classes.
-#
-# SimpleMarkup is extendable at runtime: you can add new markup
-# elements to be recognised in the documents that SimpleMarkup parses.
-#
-# SimpleMarkup is intended to be the basis for a family of tools which
-# share the common requirement that simple, plain-text should be
-# rendered in a variety of different output formats and media. It is
-# envisaged that SimpleMarkup could be the basis for formating RDoc
-# style comment blocks, Wiki entries, and online FAQs.
-#
-# = Basic Formatting
-#
-# * SimpleMarkup looks for a document's natural left margin. This is
-# used as the initial margin for the document.
-#
-# * Consecutive lines starting at this margin are considered to be a
-# paragraph.
-#
-# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
-# taken to be the start of a list. The margin in increased to be the
-# first non-space following the list start flag. Subsequent lines
-# should be indented to this new margin until the list ends. For
-# example:
-#
-# * this is a list with three paragraphs in
-# the first item. This is the first paragraph.
-#
-# And this is the second paragraph.
-#
-# 1. This is an indented, numbered list.
-# 2. This is the second item in that list
-#
-# This is the third conventional paragraph in the
-# first list item.
-#
-# * This is the second item in the original list
-#
-# * You can also construct labeled lists, sometimes called description
-# or definition lists. Do this by putting the label in square brackets
-# and indenting the list body:
-#
-# [cat] a small furry mammal
-# that seems to sleep a lot
-#
-# [ant] a little insect that is known
-# to enjoy picnics
-#
-# A minor variation on labeled lists uses two colons to separate the
-# label from the list body:
-#
-# cat:: a small furry mammal
-# that seems to sleep a lot
-#
-# ant:: a little insect that is known
-# to enjoy picnics
-#
-# This latter style guarantees that the list bodies' left margins are
-# aligned: think of them as a two column table.
-#
-# * Any line that starts to the right of the current margin is treated
-# as verbatim text. This is useful for code listings. The example of a
-# list above is also verbatim text.
-#
-# * A line starting with an equals sign (=) is treated as a
-# heading. Level one headings have one equals sign, level two headings
-# have two,and so on.
-#
-# * A line starting with three or more hyphens (at the current indent)
-# generates a horizontal rule. THe more hyphens, the thicker the rule
-# (within reason, and if supported by the output device)
-#
-# * You can use markup within text (except verbatim) to change the
-# appearance of parts of that text. Out of the box, SimpleMarkup
-# supports word-based and general markup.
-#
-# Word-based markup uses flag characters around individual words:
-#
-# [\*word*] displays word in a *bold* font
-# [\_word_] displays word in an _emphasized_ font
-# [\+word+] displays word in a +code+ font
-#
-# General markup affects text between a start delimiter and and end
-# delimiter. Not surprisingly, these delimiters look like HTML markup.
-#
-# [\<b>text...</b>] displays word in a *bold* font
-# [\<em>text...</em>] displays word in an _emphasized_ font
-# [\<i>text...</i>] displays word in an _emphasized_ font
-# [\<tt>text...</tt>] displays word in a +code+ font
-#
-# Unlike conventional Wiki markup, general markup can cross line
-# boundaries. You can turn off the interpretation of markup by
-# preceding the first character with a backslash, so \\\<b>bold
-# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold
-# respectively.
-#
-# = Using SimpleMarkup
-#
-# For information on using SimpleMarkup programatically,
-# see SM::SimpleMarkup.
-#
-# Author:: Dave Thomas, dave@pragmaticprogrammer.com
-# Version:: 0.0
-# License:: Ruby license
-
-
-
-require 'rdoc/markup/simple_markup/fragments'
-require 'rdoc/markup/simple_markup/lines.rb'
-
-module SM #:nodoc:
-
- # == Synopsis
- #
- # This code converts <tt>input_string</tt>, which is in the format
- # described in markup/simple_markup.rb, to HTML. The conversion
- # takes place in the +convert+ method, so you can use the same
- # SimpleMarkup object to convert multiple input strings.
- #
- # require 'rdoc/markup/simple_markup'
- # require 'rdoc/markup/simple_markup/to_html'
- #
- # p = SM::SimpleMarkup.new
- # h = SM::ToHtml.new
- #
- # puts p.convert(input_string, h)
- #
- # You can extend the SimpleMarkup parser to recognise new markup
- # sequences, and to add special processing for text that matches a
- # regular epxression. Here we make WikiWords significant to the parser,
- # and also make the sequences {word} and \<no>text...</no> signify
- # strike-through text. When then subclass the HTML output class to deal
- # with these:
- #
- # require 'rdoc/markup/simple_markup'
- # require 'rdoc/markup/simple_markup/to_html'
- #
- # class WikiHtml < SM::ToHtml
- # def handle_special_WIKIWORD(special)
- # "<font color=red>" + special.text + "</font>"
- # end
- # end
- #
- # p = SM::SimpleMarkup.new
- # p.add_word_pair("{", "}", :STRIKE)
- # p.add_html("no", :STRIKE)
- #
- # p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
- #
- # h = WikiHtml.new
- # h.add_tag(:STRIKE, "<strike>", "</strike>")
- #
- # puts "<body>" + p.convert(ARGF.read, h) + "</body>"
- #
- # == Output Formatters
- #
- # _missing_
- #
- #
-
- class SimpleMarkup
-
- SPACE = ?\s
-
- # List entries look like:
- # * text
- # 1. text
- # [label] text
- # label:: text
- #
- # Flag it as a list entry, and
- # work out the indent for subsequent lines
-
- SIMPLE_LIST_RE = /^(
- ( \* (?# bullet)
- |- (?# bullet)
- |\d+\. (?# numbered )
- |[A-Za-z]\. (?# alphabetically numbered )
- )
- \s+
- )\S/x
-
- LABEL_LIST_RE = /^(
- ( \[.*?\] (?# labeled )
- |\S.*:: (?# note )
- )(?:\s+|$)
- )/x
-
-
- ##
- # take a block of text and use various heuristics to determine
- # it's structure (paragraphs, lists, and so on). Invoke an
- # event handler as we identify significant chunks.
- #
-
- def initialize
- @am = AttributeManager.new
- @output = nil
- end
-
- ##
- # Add to the sequences used to add formatting to an individual word
- # (such as *bold*). Matching entries will generate attibutes
- # that the output formatters can recognize by their +name+
-
- def add_word_pair(start, stop, name)
- @am.add_word_pair(start, stop, name)
- end
-
- ##
- # Add to the sequences recognized as general markup
- #
-
- def add_html(tag, name)
- @am.add_html(tag, name)
- end
-
- ##
- # Add to other inline sequences. For example, we could add
- # WikiWords using something like:
- #
- # parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
- #
- # Each wiki word will be presented to the output formatter
- # via the accept_special method
- #
-
- def add_special(pattern, name)
- @am.add_special(pattern, name)
- end
-
-
- # We take a string, split it into lines, work out the type of
- # each line, and from there deduce groups of lines (for example
- # all lines in a paragraph). We then invoke the output formatter
- # using a Visitor to display the result
-
- def convert(str, op)
- @lines = Lines.new(str.split(/\r?\n/).collect { |aLine|
- Line.new(aLine) })
- return "" if @lines.empty?
- @lines.normalize
- assign_types_to_lines
- group = group_lines
- # call the output formatter to handle the result
- # group.to_a.each {|i| p i}
- group.accept(@am, op)
- end
-
-
- #######
- private
- #######
-
-
- ##
- # Look through the text at line indentation. We flag each line as being
- # Blank, a paragraph, a list element, or verbatim text
- #
-
- def assign_types_to_lines(margin = 0, level = 0)
-
- while line = @lines.next
- if line.isBlank?
- line.stamp(Line::BLANK, level)
- next
- end
-
- # if a line contains non-blanks before the margin, then it must belong
- # to an outer level
-
- text = line.text
-
- for i in 0...margin
- if text[i] != SPACE
- @lines.unget
- return
- end
- end
-
- active_line = text[margin..-1]
-
- # Rules (horizontal lines) look like
- #
- # --- (three or more hyphens)
- #
- # The more hyphens, the thicker the rule
- #
-
- if /^(---+)\s*$/ =~ active_line
- line.stamp(Line::RULE, level, $1.length-2)
- next
- end
-
- # Then look for list entries. First the ones that have to have
- # text following them (* xxx, - xxx, and dd. xxx)
-
- if SIMPLE_LIST_RE =~ active_line
-
- offset = margin + $1.length
- prefix = $2
- prefix_length = prefix.length
-
- flag = case prefix
- when "*","-" then ListBase::BULLET
- when /^\d/ then ListBase::NUMBER
- when /^[A-Z]/ then ListBase::UPPERALPHA
- when /^[a-z]/ then ListBase::LOWERALPHA
- else raise "Invalid List Type: #{self.inspect}"
- end
-
- line.stamp(Line::LIST, level+1, prefix, flag)
- text[margin, prefix_length] = " " * prefix_length
- assign_types_to_lines(offset, level + 1)
- next
- end
-
-
- if LABEL_LIST_RE =~ active_line
- offset = margin + $1.length
- prefix = $2
- prefix_length = prefix.length
-
- next if handled_labeled_list(line, level, margin, offset, prefix)
- end
-
- # Headings look like
- # = Main heading
- # == Second level
- # === Third
- #
- # Headings reset the level to 0
-
- if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
- prefix_length = $1.length
- prefix_length = 6 if prefix_length > 6
- line.stamp(Line::HEADING, 0, prefix_length)
- line.strip_leading(margin + prefix_length)
- next
- end
-
- # If the character's a space, then we have verbatim text,
- # otherwise
-
- if active_line[0] == SPACE
- line.strip_leading(margin) if margin > 0
- line.stamp(Line::VERBATIM, level)
- else
- line.stamp(Line::PARAGRAPH, level)
- end
- end
- end
-
- # Handle labeled list entries, We have a special case
- # to deal with. Because the labels can be long, they force
- # the remaining block of text over the to right:
- #
- # this is a long label that I wrote:: and here is the
- # block of text with
- # a silly margin
- #
- # So we allow the special case. If the label is followed
- # by nothing, and if the following line is indented, then
- # we take the indent of that line as the new margin
- #
- # this is a long label that I wrote::
- # here is a more reasonably indented block which
- # will ab attached to the label.
- #
-
- def handled_labeled_list(line, level, margin, offset, prefix)
- prefix_length = prefix.length
- text = line.text
- flag = nil
- case prefix
- when /^\[/
- flag = ListBase::LABELED
- prefix = prefix[1, prefix.length-2]
- when /:$/
- flag = ListBase::NOTE
- prefix.chop!
- else raise "Invalid List Type: #{self.inspect}"
- end
-
- # body is on the next line
-
- if text.length <= offset
- original_line = line
- line = @lines.next
- return(false) unless line
- text = line.text
-
- for i in 0..margin
- if text[i] != SPACE
- @lines.unget
- return false
- end
- end
- i = margin
- i += 1 while text[i] == SPACE
- if i >= text.length
- @lines.unget
- return false
- else
- offset = i
- prefix_length = 0
- @lines.delete(original_line)
- end
- end
-
- line.stamp(Line::LIST, level+1, prefix, flag)
- text[margin, prefix_length] = " " * prefix_length
- assign_types_to_lines(offset, level + 1)
- return true
- end
-
- # Return a block consisting of fragments which are
- # paragraphs, list entries or verbatim text. We merge consecutive
- # lines of the same type and level together. We are also slightly
- # tricky with lists: the lines following a list introduction
- # look like paragraph lines at the next level, and we remap them
- # into list entries instead
-
- def group_lines
- @lines.rewind
-
- inList = false
- wantedType = wantedLevel = nil
-
- block = LineCollection.new
- group = nil
-
- while line = @lines.next
- if line.level == wantedLevel and line.type == wantedType
- group.add_text(line.text)
- else
- group = block.fragment_for(line)
- block.add(group)
- if line.type == Line::LIST
- wantedType = Line::PARAGRAPH
- else
- wantedType = line.type
- end
- wantedLevel = line.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
diff --git a/lib/rdoc/markup/simple_markup/fragments.rb b/lib/rdoc/markup/simple_markup/fragments.rb
deleted file mode 100644
index 6ca06382ab..0000000000
--- a/lib/rdoc/markup/simple_markup/fragments.rb
+++ /dev/null
@@ -1,328 +0,0 @@
-require 'rdoc/markup/simple_markup/lines.rb'
-#require 'rdoc/markup/simple_markup/to_flow.rb'
-
-module SM
-
- ##
- # A Fragment is a chunk of text, subclassed as a paragraph, a list
- # entry, or verbatim text
-
- class Fragment
- attr_reader :level, :param, :txt
- attr_accessor :type
-
- def initialize(level, param, type, txt)
- @level = level
- @param = param
- @type = type
- @txt = ""
- add_text(txt) if txt
- end
-
- def add_text(txt)
- @txt << " " if @txt.length > 0
- @txt << txt.tr_s("\n ", " ").strip
- end
-
- def to_s
- "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
- end
-
- ######
- # This is a simple factory system that lets us associate fragement
- # types (a string) with a subclass of fragment
-
- TYPE_MAP = {}
-
- def Fragment.type_name(name)
- TYPE_MAP[name] = self
- end
-
- def Fragment.for(line)
- klass = TYPE_MAP[line.type] ||
- raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
- return klass.new(line.level, line.param, line.flag, line.text)
- end
- end
-
- ##
- # A paragraph is a fragment which gets wrapped to fit. We remove all
- # newlines when we're created, and have them put back on output
-
- class Paragraph < Fragment
- type_name Line::PARAGRAPH
- end
-
- class BlankLine < Paragraph
- type_name Line::BLANK
- end
-
- class Heading < Paragraph
- type_name Line::HEADING
-
- def head_level
- @param.to_i
- end
- end
-
- ##
- # A List is a fragment with some kind of label
- #
-
- class ListBase < Paragraph
- # List types
- BULLET = :BULLET
- NUMBER = :NUMBER
- UPPERALPHA = :UPPERALPHA
- LOWERALPHA = :LOWERALPHA
- LABELED = :LABELED
- NOTE = :NOTE
- end
-
- class ListItem < ListBase
- type_name Line::LIST
-
- # def label
- # am = AttributeManager.new(@param)
- # am.flow
- # end
- end
-
- class ListStart < ListBase
- def initialize(level, param, type)
- super(level, param, type, nil)
- end
- end
-
- class ListEnd < ListBase
- def initialize(level, type)
- super(level, "", type, nil)
- end
- end
-
- ##
- # Verbatim code contains lines that don't get wrapped.
-
- class Verbatim < Fragment
- type_name Line::VERBATIM
-
- def add_text(txt)
- @txt << txt.chomp << "\n"
- end
-
- end
-
- ##
- # A horizontal rule
- class Rule < Fragment
- type_name Line::RULE
- end
-
-
- # Collect groups of lines together. Each group
- # will end up containing a flow of text
-
- class LineCollection
-
- def initialize
- @fragments = []
- end
-
- def add(fragment)
- @fragments << fragment
- end
-
- def each(&b)
- @fragments.each(&b)
- end
-
- # For testing
- def to_a
- @fragments.map {|fragment| fragment.to_s}
- end
-
- # Factory for different fragment types
- def fragment_for(*args)
- Fragment.for(*args)
- end
-
- # tidy up at the end
- def normalize
- change_verbatim_blank_lines
- add_list_start_and_ends
- add_list_breaks
- tidy_blank_lines
- end
-
- def to_s
- @fragments.join("\n----\n")
- end
-
- def accept(am, visitor)
-
- visitor.start_accepting
-
- @fragments.each do |fragment|
- case fragment
- when Verbatim
- visitor.accept_verbatim(am, fragment)
- when Rule
- visitor.accept_rule(am, fragment)
- when ListStart
- visitor.accept_list_start(am, fragment)
- when ListEnd
- visitor.accept_list_end(am, fragment)
- when ListItem
- visitor.accept_list_item(am, fragment)
- when BlankLine
- visitor.accept_blank_line(am, fragment)
- when Heading
- visitor.accept_heading(am, fragment)
- when Paragraph
- visitor.accept_paragraph(am, fragment)
- end
- end
-
- visitor.end_accepting
- end
- #######
- private
- #######
-
- # If you have:
- #
- # normal paragraph text.
- #
- # this is code
- #
- # and more code
- #
- # You'll end up with the fragments Paragraph, BlankLine,
- # Verbatim, BlankLine, Verbatim, BlankLine, etc
- #
- # The BlankLine in the middle of the verbatim chunk needs to
- # be changed to a real verbatim newline, and the two
- # verbatim blocks merged
- #
- #
- def change_verbatim_blank_lines
- frag_block = nil
- blank_count = 0
- @fragments.each_with_index do |frag, i|
- if frag_block.nil?
- frag_block = frag if Verbatim === frag
- else
- case frag
- when Verbatim
- blank_count.times { frag_block.add_text("\n") }
- blank_count = 0
- frag_block.add_text(frag.txt)
- @fragments[i] = nil # remove out current fragment
- when BlankLine
- if frag_block
- blank_count += 1
- @fragments[i] = nil
- end
- else
- frag_block = nil
- blank_count = 0
- end
- end
- end
- @fragments.compact!
- end
-
- # List nesting is implicit given the level of
- # Make it explicit, just to make life a tad
- # easier for the output processors
-
- def add_list_start_and_ends
- level = 0
- res = []
- type_stack = []
-
- @fragments.each do |fragment|
- # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
- new_level = fragment.level
- while (level < new_level)
- level += 1
- type = fragment.type
- res << ListStart.new(level, fragment.param, type) if type
- type_stack.push type
- # $stderr.puts "Start: #{level}"
- end
-
- while level > new_level
- type = type_stack.pop
- res << ListEnd.new(level, type) if type
- level -= 1
- # $stderr.puts "End: #{level}, #{type}"
- end
-
- res << fragment
- level = fragment.level
- end
- level.downto(1) do |i|
- type = type_stack.pop
- res << ListEnd.new(i, type) if type
- end
-
- @fragments = res
- end
-
- # now insert start/ends between list entries at the
- # same level that have different element types
-
- def add_list_breaks
- res = @fragments
-
- @fragments = []
- list_stack = []
-
- res.each do |fragment|
- case fragment
- when ListStart
- list_stack.push fragment
- when ListEnd
- start = list_stack.pop
- fragment.type = start.type
- when ListItem
- l = list_stack.last
- if fragment.type != l.type
- @fragments << ListEnd.new(l.level, l.type)
- start = ListStart.new(l.level, fragment.param, fragment.type)
- @fragments << start
- list_stack.pop
- list_stack.push start
- end
- else
- ;
- end
- @fragments << fragment
- end
- end
-
- # Finally tidy up the blank lines:
- # * change Blank/ListEnd into ListEnd/Blank
- # * remove blank lines at the front
-
- def tidy_blank_lines
- (@fragments.size - 1).times do |i|
- if @fragments[i].kind_of?(BlankLine) and
- @fragments[i+1].kind_of?(ListEnd)
- @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
- end
- end
-
- # remove leading blanks
- @fragments.each_with_index do |f, i|
- break unless f.kind_of? BlankLine
- @fragments[i] = nil
- end
-
- @fragments.compact!
- end
-
- end
-
-end
diff --git a/lib/rdoc/markup/simple_markup/inline.rb b/lib/rdoc/markup/simple_markup/inline.rb
deleted file mode 100644
index d54fe1e667..0000000000
--- a/lib/rdoc/markup/simple_markup/inline.rb
+++ /dev/null
@@ -1,340 +0,0 @@
-module SM
-
- # We manage a set of attributes. Each attribute has a symbol name
- # and a bit value
-
- class Attribute
- SPECIAL = 1
-
- @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
- @@next_bitmap = 2
-
- def Attribute.bitmap_for(name)
- bitmap = @@name_to_bitmap[name]
- if !bitmap
- bitmap = @@next_bitmap
- @@next_bitmap <<= 1
- @@name_to_bitmap[name] = bitmap
- end
- bitmap
- end
-
- def Attribute.as_string(bitmap)
- return "none" if bitmap.zero?
- res = []
- @@name_to_bitmap.each do |name, bit|
- res << name if (bitmap & bit) != 0
- end
- res.join(",")
- end
-
- def Attribute.each_name_of(bitmap)
- @@name_to_bitmap.each do |name, bit|
- next if bit == SPECIAL
- yield name.to_s if (bitmap & bit) != 0
- end
- end
- end
-
-
- # An AttrChanger records a change in attributes. It contains
- # a bitmap of the attributes to turn on, and a bitmap of those to
- # turn off
-
- AttrChanger = Struct.new(:turn_on, :turn_off)
- class AttrChanger
- def to_s
- "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
- end
- end
-
- # An array of attributes which parallels the characters in a string
- class AttrSpan
- def initialize(length)
- @attrs = Array.new(length, 0)
- end
-
- def set_attrs(start, length, bits)
- for i in start ... (start+length)
- @attrs[i] |= bits
- end
- end
-
- def [](n)
- @attrs[n]
- end
- end
-
- ##
- # Hold details of a special sequence
-
- class Special
- attr_reader :type
- attr_accessor :text
-
- def initialize(type, text)
- @type, @text = type, text
- end
-
- def ==(o)
- self.text == o.text && self.type == o.type
- end
-
- def to_s
- "Special: type=#{type}, text=#{text.dump}"
- end
- end
-
- class AttributeManager
-
- NULL = "\000".freeze
-
- ##
- # We work by substituting non-printing characters in to the
- # text. For now I'm assuming that I can substitute
- # a character in the range 0..8 for a 7 bit character
- # without damaging the encoded string, but this might
- # be optimistic
- #
-
- A_PROTECT = 004
- PROTECT_ATTR = A_PROTECT.chr
-
- # This maps delimiters that occur around words (such as
- # *bold* or +tt+) where the start and end delimiters
- # and the same. This lets us optimize the regexp
- MATCHING_WORD_PAIRS = {}
-
- # And this is used when the delimiters aren't the same. In this
- # case the hash maps a pattern to the attribute character
- WORD_PAIR_MAP = {}
-
- # This maps HTML tags to the corresponding attribute char
- HTML_TAGS = {}
-
- # And this maps _special_ sequences to a name. A special sequence
- # is something like a WikiWord
- SPECIAL = {}
-
- # Return an attribute object with the given turn_on
- # and turn_off bits set
-
- def attribute(turn_on, turn_off)
- AttrChanger.new(turn_on, turn_off)
- end
-
-
- def change_attribute(current, new)
- diff = current ^ new
- attribute(new & diff, current & diff)
- end
-
- def changed_attribute_by_name(current_set, new_set)
- current = new = 0
- current_set.each {|name| current |= Attribute.bitmap_for(name) }
- new_set.each {|name| new |= Attribute.bitmap_for(name) }
- change_attribute(current, new)
- end
-
- def copy_string(start_pos, end_pos)
- res = @str[start_pos...end_pos]
- res.gsub!(/\000/, '')
- res
- end
-
- # Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
- # where <char> is a per-attribute specific character
-
- def convert_attrs(str, attrs)
- # first do matching ones
- tags = MATCHING_WORD_PAIRS.keys.join("")
- re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
-# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
- 1 while str.gsub!(Regexp.new(re)) {
- attr = MATCHING_WORD_PAIRS[$2];
- attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
- $1 + NULL*$2.length + $3 + NULL*$2.length + $4
- }
-
- # then non-matching
- unless WORD_PAIR_MAP.empty?
- WORD_PAIR_MAP.each do |regexp, attr|
- str.gsub!(regexp) {
- attrs.set_attrs($`.length + $1.length, $2.length, attr)
- NULL*$1.length + $2 + NULL*$3.length
- }
- end
- end
- end
-
- def convert_html(str, attrs)
- tags = HTML_TAGS.keys.join("|")
- re = "<(#{tags})>(.*?)</\\1>"
- 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
- attr = HTML_TAGS[$1.downcase]
- html_length = $1.length + 2
- seq = NULL * html_length
- attrs.set_attrs($`.length + html_length, $2.length, attr)
- seq + $2 + seq + NULL
- }
- end
-
- def convert_specials(str, attrs)
- unless SPECIAL.empty?
- SPECIAL.each do |regexp, attr|
- str.scan(regexp) do
- attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
- end
- end
- end
- end
-
- # A \ in front of a character that would normally be
- # processed turns off processing. We do this by turning
- # \< into <#{PROTECT}
-
- PROTECTABLE = [ "<" << "\\" ] #"
-
-
- def mask_protected_sequences
- protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
- @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
- end
-
- def unmask_protected_sequences
- @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
- end
-
- def initialize
- add_word_pair("*", "*", :BOLD)
- add_word_pair("_", "_", :EM)
- add_word_pair("+", "+", :TT)
-
- add_html("em", :EM)
- add_html("i", :EM)
- add_html("b", :BOLD)
- add_html("tt", :TT)
- add_html("code", :TT)
-
- add_special(/<!--(.*?)-->/, :COMMENT)
- end
-
- def add_word_pair(start, stop, name)
- raise "Word flags may not start '<'" if start[0] == ?<
- bitmap = Attribute.bitmap_for(name)
- if start == stop
- MATCHING_WORD_PAIRS[start] = bitmap
- else
- pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
-# "([A-Za-z]+)" +
- "(\\S+)" +
- "(" + Regexp.escape(stop) +")")
- WORD_PAIR_MAP[pattern] = bitmap
- end
- PROTECTABLE << start[0,1]
- PROTECTABLE.uniq!
- end
-
- def add_html(tag, name)
- HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
- end
-
- def add_special(pattern, name)
- SPECIAL[pattern] = Attribute.bitmap_for(name)
- end
-
- def flow(str)
- @str = str
-
- puts("Before flow, str='#{@str.dump}'") if $DEBUG
- mask_protected_sequences
-
- @attrs = AttrSpan.new(@str.length)
-
- puts("After protecting, str='#{@str.dump}'") if $DEBUG
- convert_attrs(@str, @attrs)
- convert_html(@str, @attrs)
- convert_specials(str, @attrs)
- unmask_protected_sequences
- puts("After flow, str='#{@str.dump}'") if $DEBUG
- return split_into_flow
- end
-
- def display_attributes
- puts
- puts @str.tr(NULL, "!")
- bit = 1
- 16.times do |bno|
- line = ""
- @str.length.times do |i|
- if (@attrs[i] & bit) == 0
- line << " "
- else
- if bno.zero?
- line << "S"
- else
- line << ("%d" % (bno+1))
- end
- end
- end
- puts(line) unless line =~ /^ *$/
- bit <<= 1
- end
- end
-
- def split_into_flow
-
- display_attributes if $DEBUG
-
- res = []
- current_attr = 0
- str = ""
-
-
- str_len = @str.length
-
- # skip leading invisible text
- i = 0
- i += 1 while i < str_len and @str[i].zero?
- start_pos = i
-
- # then scan the string, chunking it on attribute changes
- while i < str_len
- new_attr = @attrs[i]
- if new_attr != current_attr
- if i > start_pos
- res << copy_string(start_pos, i)
- start_pos = i
- end
-
- res << change_attribute(current_attr, new_attr)
- current_attr = new_attr
-
- if (current_attr & Attribute::SPECIAL) != 0
- i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
- res << Special.new(current_attr, copy_string(start_pos, i))
- start_pos = i
- next
- end
- end
-
- # move on, skipping any invisible characters
- begin
- i += 1
- end while i < str_len and @str[i].zero?
- end
-
- # tidy up trailing text
- if start_pos < str_len
- res << copy_string(start_pos, str_len)
- end
-
- # and reset to all attributes off
- res << change_attribute(current_attr, 0) if current_attr != 0
-
- return res
- end
-
- end
-
-end
diff --git a/lib/rdoc/markup/simple_markup/lines.rb b/lib/rdoc/markup/simple_markup/lines.rb
deleted file mode 100644
index 4e294f27dc..0000000000
--- a/lib/rdoc/markup/simple_markup/lines.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-##########################################################################
-#
-# We store the lines we're working on as objects of class Line.
-# These contain the text of the line, along with a flag indicating the
-# line type, and an indentation level
-
-module SM
-
- class Line
- INFINITY = 9999
-
- BLANK = :BLANK
- HEADING = :HEADING
- LIST = :LIST
- RULE = :RULE
- PARAGRAPH = :PARAGRAPH
- VERBATIM = :VERBATIM
-
- # line type
- attr_accessor :type
-
- # The indentation nesting level
- attr_accessor :level
-
- # The contents
- attr_accessor :text
-
- # A prefix or parameter. For LIST lines, this is
- # the text that introduced the list item (the label)
- attr_accessor :param
-
- # A flag. For list lines, this is the type of the list
- attr_accessor :flag
-
- # the number of leading spaces
- attr_accessor :leading_spaces
-
- # true if this line has been deleted from the list of lines
- attr_accessor :deleted
-
-
- def initialize(text)
- @text = text.dup
- @deleted = false
-
- # expand tabs
- 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
-
- # Strip trailing whitespace
- @text.sub!(/\s+$/, '')
-
- # and look for leading whitespace
- if @text.length > 0
- @text =~ /^(\s*)/
- @leading_spaces = $1.length
- else
- @leading_spaces = INFINITY
- end
- end
-
- # Return true if this line is blank
- def isBlank?
- @text.length.zero?
- end
-
- # stamp a line with a type, a level, a prefix, and a flag
- def stamp(type, level, param="", flag=nil)
- @type, @level, @param, @flag = type, level, param, flag
- end
-
- ##
- # Strip off the leading margin
- #
-
- def strip_leading(size)
- if @text.size > size
- @text[0,size] = ""
- else
- @text = ""
- end
- end
-
- def to_s
- "#@type#@level: #@text"
- end
- end
-
- ###############################################################################
- #
- # A container for all the lines
- #
-
- class Lines
- include Enumerable
-
- attr_reader :lines # for debugging
-
- def initialize(lines)
- @lines = lines
- rewind
- end
-
- def empty?
- @lines.size.zero?
- end
-
- def each
- @lines.each do |line|
- yield line unless line.deleted
- end
- end
-
-# def [](index)
-# @lines[index]
-# end
-
- def rewind
- @nextline = 0
- end
-
- def next
- begin
- res = @lines[@nextline]
- @nextline += 1 if @nextline < @lines.size
- end while res and res.deleted and @nextline < @lines.size
- res
- end
-
- def unget
- @nextline -= 1
- end
-
- def delete(a_line)
- a_line.deleted = true
- end
-
- def normalize
- margin = @lines.collect{|l| l.leading_spaces}.min
- margin = 0 if margin == Line::INFINITY
- @lines.each {|line| line.strip_leading(margin) } if margin > 0
- end
-
- def as_text
- @lines.map {|l| l.text}.join("\n")
- end
-
- def line_types
- @lines.map {|l| l.type }
- end
- end
-end
diff --git a/lib/rdoc/markup/simple_markup/preprocess.rb b/lib/rdoc/markup/simple_markup/preprocess.rb
deleted file mode 100644
index 101c9bdeb1..0000000000
--- a/lib/rdoc/markup/simple_markup/preprocess.rb
+++ /dev/null
@@ -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
diff --git a/lib/rdoc/markup/simple_markup/to_flow.rb b/lib/rdoc/markup/simple_markup/to_flow.rb
deleted file mode 100644
index 048e71abce..0000000000
--- a/lib/rdoc/markup/simple_markup/to_flow.rb
+++ /dev/null
@@ -1,188 +0,0 @@
-require 'rdoc/markup/simple_markup/fragments'
-require 'rdoc/markup/simple_markup/inline'
-require 'cgi'
-
-module SM
-
- module Flow
- P = Struct.new(:body)
- VERB = Struct.new(:body)
- RULE = Struct.new(:width)
- class LIST
- attr_reader :type, :contents
- def initialize(type)
- @type = type
- @contents = []
- end
- def <<(stuff)
- @contents << stuff
- end
- end
- LI = Struct.new(:label, :body)
- H = Struct.new(:level, :text)
- end
-
- class ToFlow
- 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>" ],
- }
-
- 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 = []
- @list_stack = []
- end
-
- def end_accepting
- @res
- end
-
- def accept_paragraph(am, fragment)
- @res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
- end
-
- def accept_verbatim(am, fragment)
- @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
- end
-
- def accept_rule(am, fragment)
- size = fragment.param
- size = 10 if size > 10
- @res << Flow::RULE.new(size)
- end
-
- def accept_list_start(am, fragment)
- @list_stack.push(@res)
- list = Flow::LIST.new(fragment.type)
- @res << list
- @res = list
- end
-
- def accept_list_end(am, fragment)
- @res = @list_stack.pop
- end
-
- def accept_list_item(am, fragment)
- @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
- end
-
- def accept_blank_line(am, fragment)
- # @res << annotate("<p />") << "\n"
- end
-
- def accept_heading(am, fragment)
- @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
- end
-
-
- #######################################################################
-
- private
-
- #######################################################################
-
- def on_tags(res, item)
- attr_mask = item.turn_on
- return if attr_mask.zero?
-
- @attr_tags.each do |tag|
- if attr_mask & tag.bit != 0
- res << annotate(tag.on)
- end
- end
- end
-
- def off_tags(res, item)
- attr_mask = item.turn_off
- return if attr_mask.zero?
-
- @attr_tags.reverse_each do |tag|
- if attr_mask & tag.bit != 0
- res << annotate(tag.off)
- end
- end
- end
-
- def convert_flow(flow)
- res = ""
- flow.each do |item|
- case item
- when String
- res << convert_string(item)
- when AttrChanger
- off_tags(res, item)
- on_tags(res, item)
- when Special
- res << convert_special(item)
- else
- raise "Unknown flow element: #{item.inspect}"
- end
- end
- res
- end
-
- # some of these patterns are taken from SmartyPants...
-
- def convert_string(item)
- CGI.escapeHTML(item)
- end
-
- def convert_special(special)
- handled = false
- Attribute.each_name_of(special.type) do |name|
- method_name = "handle_special_#{name}"
- if self.respond_to? method_name
- special.text = send(method_name, special)
- handled = true
- end
- end
- raise "Unhandled special: #{special}" unless handled
- special.text
- end
-
-
- end
-
-end
diff --git a/lib/rdoc/markup/simple_markup/to_html.rb b/lib/rdoc/markup/simple_markup/to_html.rb
deleted file mode 100644
index 26b5f4ce70..0000000000
--- a/lib/rdoc/markup/simple_markup/to_html.rb
+++ /dev/null
@@ -1,289 +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(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
-
- # convert ... to elipsis (and make sure .... becomes .<elipsis>)
- gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
-
- # convert single closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
- gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
-
- # convert single opening quote
- gsub(/'/, '&#8216;').
-
- # convert double closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
-
- # convert double opening quote
- gsub(/'/, '&#8220;').
-
- # convert copyright
- gsub(/\(c\)/, '&#169;').
-
- # convert and registered trademark
- gsub(/\(r\)/, '&#174;')
-
- 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
diff --git a/lib/rdoc/markup/simple_markup/to_latex.rb b/lib/rdoc/markup/simple_markup/to_latex.rb
deleted file mode 100644
index 6c16278652..0000000000
--- a/lib/rdoc/markup/simple_markup/to_latex.rb
+++ /dev/null
@@ -1,333 +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
diff --git a/lib/rdoc/markup/special.rb b/lib/rdoc/markup/special.rb
new file mode 100644
index 0000000000..57261b44a7
--- /dev/null
+++ b/lib/rdoc/markup/special.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+##
+# Hold details of a special sequence
+
+class RDoc::Markup::Special
+
+ ##
+ # Special type
+
+ attr_reader :type
+
+ ##
+ # Special text
+
+ attr_accessor :text
+
+ ##
+ # Creates a new special sequence of +type+ with +text+
+
+ def initialize(type, text)
+ @type, @text = type, text
+ end
+
+ ##
+ # Specials are equal when the have the same text and type
+
+ def ==(o)
+ self.text == o.text && self.type == o.type
+ end
+
+ def inspect # :nodoc:
+ "#<RDoc::Markup::Special:0x%x @type=%p, @text=%p>" % [
+ object_id, @type, text.dump]
+ end
+
+ def to_s # :nodoc:
+ "Special: type=#{type} text=#{text.dump}"
+ end
+
+end
+
diff --git a/lib/rdoc/markup/test/AllTests.rb b/lib/rdoc/markup/test/AllTests.rb
deleted file mode 100644
index b9c8c9dfcc..0000000000
--- a/lib/rdoc/markup/test/AllTests.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-require 'TestParse.rb'
-require 'TestInline.rb'
diff --git a/lib/rdoc/markup/test/TestInline.rb b/lib/rdoc/markup/test/TestInline.rb
deleted file mode 100644
index a067d4c24c..0000000000
--- a/lib/rdoc/markup/test/TestInline.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-require "test/unit"
-
-$:.unshift "../../.."
-
-require "rdoc/markup/simple_markup/inline"
-
-class TestInline < Test::Unit::TestCase
-
-
- def setup
- @am = SM::AttributeManager.new
-
- @bold_on = @am.changed_attribute_by_name([], [:BOLD])
- @bold_off = @am.changed_attribute_by_name([:BOLD], [])
-
- @tt_on = @am.changed_attribute_by_name([], [:TT])
- @tt_off = @am.changed_attribute_by_name([:TT], [])
-
- @em_on = @am.changed_attribute_by_name([], [:EM])
- @em_off = @am.changed_attribute_by_name([:EM], [])
-
- @bold_em_on = @am.changed_attribute_by_name([], [:BOLD] | [:EM])
- @bold_em_off = @am.changed_attribute_by_name([:BOLD] | [:EM], [])
-
- @em_then_bold = @am.changed_attribute_by_name([:EM], [:EM] | [:BOLD])
-
- @em_to_bold = @am.changed_attribute_by_name([:EM], [:BOLD])
-
- @am.add_word_pair("{", "}", :WOMBAT)
- @wombat_on = @am.changed_attribute_by_name([], [:WOMBAT])
- @wombat_off = @am.changed_attribute_by_name([:WOMBAT], [])
- end
-
- def crossref(text)
- [ @am.changed_attribute_by_name([], [:CROSSREF] | [:_SPECIAL_]),
- SM::Special.new(33, text),
- @am.changed_attribute_by_name([:CROSSREF] | [:_SPECIAL_], [])
- ]
- end
-
- def test_special
- # class names, variable names, file names, or instance variables
- @am.add_special(/(
- \b([A-Z]\w+(::\w+)*)
- | \#\w+[!?=]?
- | \b\w+([_\/\.]+\w+)+[!?=]?
- )/x,
- :CROSSREF)
-
- assert_equal(["cat"], @am.flow("cat"))
-
- assert_equal(["cat ", crossref("#fred"), " dog"].flatten,
- @am.flow("cat #fred dog"))
-
- assert_equal([crossref("#fred"), " dog"].flatten,
- @am.flow("#fred dog"))
-
- assert_equal(["cat ", crossref("#fred")].flatten, @am.flow("cat #fred"))
- end
-
- def test_basic
- assert_equal(["cat"], @am.flow("cat"))
-
- assert_equal(["cat ", @bold_on, "and", @bold_off, " dog"],
- @am.flow("cat *and* dog"))
-
- assert_equal(["cat ", @bold_on, "AND", @bold_off, " dog"],
- @am.flow("cat *AND* dog"))
-
- assert_equal(["cat ", @em_on, "And", @em_off, " dog"],
- @am.flow("cat _And_ dog"))
-
- assert_equal(["cat *and dog*"], @am.flow("cat *and dog*"))
-
- assert_equal(["*cat and* dog"], @am.flow("*cat and* dog"))
-
- assert_equal(["cat *and ", @bold_on, "dog", @bold_off],
- @am.flow("cat *and *dog*"))
-
- assert_equal(["cat ", @em_on, "and", @em_off, " dog"],
- @am.flow("cat _and_ dog"))
-
- assert_equal(["cat_and_dog"],
- @am.flow("cat_and_dog"))
-
- assert_equal(["cat ", @tt_on, "and", @tt_off, " dog"],
- @am.flow("cat +and+ dog"))
-
- assert_equal(["cat ", @bold_on, "a_b_c", @bold_off, " dog"],
- @am.flow("cat *a_b_c* dog"))
-
- assert_equal(["cat __ dog"],
- @am.flow("cat __ dog"))
-
- assert_equal(["cat ", @em_on, "_", @em_off, " dog"],
- @am.flow("cat ___ dog"))
-
- end
-
- def test_combined
- assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
- @am.flow("cat _and_ *dog*"))
-
- assert_equal(["cat ", @em_on, "a__nd", @em_off, " ", @bold_on, "dog", @bold_off],
- @am.flow("cat _a__nd_ *dog*"))
- end
-
- def test_html_like
- assert_equal(["cat ", @tt_on, "dog", @tt_off], @am.flow("cat <tt>dog</Tt>"))
-
- assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
- @am.flow("cat <i>and</i> <B>dog</b>"))
-
- assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
- @am.flow("cat <i>and <B>dog</B></I>"))
-
- assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
- @am.flow("cat <i>and </i><b>dog</b>"))
-
- assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
- @am.flow("cat <i>and <b></i>dog</b>"))
-
- assert_equal([@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
- @am.flow("<tt>cat</tt> <i>and <b></i>dog</b>"))
-
- assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
- @am.flow("cat <i>and <b>dog</b></i>"))
-
- assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
- @am.flow("cat <i><b>and</b></i> dog"))
-
-
- end
-
- def test_protect
- assert_equal(['cat \\ dog'], @am.flow('cat \\ dog'))
-
- assert_equal(["cat <tt>dog</Tt>"], @am.flow("cat \\<tt>dog</Tt>"))
-
- assert_equal(["cat ", @em_on, "and", @em_off, " <B>dog</b>"],
- @am.flow("cat <i>and</i> \\<B>dog</b>"))
-
- assert_equal(["*word* or <b>text</b>"], @am.flow("\\*word* or \\<b>text</b>"))
-
- assert_equal(["_cat_", @em_on, "dog", @em_off],
- @am.flow("\\_cat_<i>dog</i>"))
- end
-
- def test_adding
- assert_equal(["cat ", @wombat_on, "and", @wombat_off, " dog" ],
- @am.flow("cat {and} dog"))
-# assert_equal(["cat {and} dog" ], @am.flow("cat \\{and} dog"))
- end
-end
diff --git a/lib/rdoc/markup/test/TestParse.rb b/lib/rdoc/markup/test/TestParse.rb
deleted file mode 100644
index 3ec541ce7a..0000000000
--- a/lib/rdoc/markup/test/TestParse.rb
+++ /dev/null
@@ -1,503 +0,0 @@
-require 'test/unit'
-
-$:.unshift "../../.."
-
-require 'rdoc/markup/simple_markup'
-
-include SM
-
-class TestParse < Test::Unit::TestCase
-
- class MockOutput
- def start_accepting
- @res = []
- end
-
- def end_accepting
- @res
- end
-
- def accept_paragraph(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_verbatim(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_list_start(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_list_end(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_list_item(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_blank_line(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_heading(am, fragment)
- @res << fragment.to_s
- end
-
- def accept_rule(am, fragment)
- @res << fragment.to_s
- end
-
- end
-
- def basic_conv(str)
- sm = SimpleMarkup.new
- mock = MockOutput.new
- sm.convert(str, mock)
- sm.content
- end
-
- def line_types(str, expected)
- p = SimpleMarkup.new
- mock = MockOutput.new
- p.convert(str, mock)
- assert_equal(expected, p.get_line_types.map{|type| type.to_s[0,1]}.join(''))
- end
-
- def line_groups(str, expected)
- p = SimpleMarkup.new
- mock = MockOutput.new
-
- block = p.convert(str, mock)
-
- if block != expected
- rows = (0...([expected.size, block.size].max)).collect{|i|
- [expected[i]||"nil", block[i]||"nil"]
- }
- printf "\n\n%35s %35s\n", "Expected", "Got"
- rows.each {|e,g| printf "%35s %35s\n", e.dump, g.dump }
- end
-
- assert_equal(expected, block)
- end
-
- def test_tabs
- str = "hello\n dave"
- assert_equal(str, basic_conv(str))
- str = "hello\n\tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = "hello\n \tdave"
- assert_equal("hello\n dave", basic_conv(str))
- str = ".\t\t."
- assert_equal(". .", basic_conv(str))
- end
-
- def test_whitespace
- assert_equal("hello", basic_conv("hello"))
- assert_equal("hello", basic_conv(" hello "))
- assert_equal("hello", basic_conv(" \t \t hello\t\t"))
-
- assert_equal("1\n 2\n 3", basic_conv("1\n 2\n 3"))
- assert_equal("1\n 2\n 3", basic_conv(" 1\n 2\n 3"))
-
- assert_equal("1\n 2\n 3\n1\n 2", basic_conv("1\n 2\n 3\n1\n 2"))
- assert_equal("1\n 2\n 3\n1\n 2", basic_conv(" 1\n 2\n 3\n 1\n 2"))
-
- assert_equal("1\n 2\n\n 3", basic_conv(" 1\n 2\n\n 3"))
- end
-
- def test_types
- str = "now is the time"
- line_types(str, 'P')
-
- str = "now is the time\nfor all good men"
- line_types(str, 'PP')
-
- str = "now is the time\n code\nfor all good men"
- line_types(str, 'PVP')
-
- str = "now is the time\n code\n more code\nfor all good men"
- line_types(str, 'PVVP')
-
- str = "now is\n---\nthe time"
- line_types(str, 'PRP')
-
- str = %{\
- now is
- * l1
- * l2
- the time}
- line_types(str, 'PLLP')
-
- str = %{\
- now is
- * l1
- l1+
- * l2
- the time}
- line_types(str, 'PLPLP')
-
- str = %{\
- now is
- * l1
- * l1.1
- * l2
- the time}
- line_types(str, 'PLLLP')
-
- str = %{\
- now is
- * l1
- * l1.1
- text
- code
- code
-
- text
- * l2
- the time}
- line_types(str, 'PLLPVVBPLP')
-
- str = %{\
- now is
- 1. l1
- * l1.1
- 2. l2
- the time}
- line_types(str, 'PLLLP')
-
- str = %{\
- now is
- [cat] l1
- * l1.1
- [dog] l2
- the time}
- line_types(str, 'PLLLP')
-
- str = %{\
- now is
- [cat] l1
- continuation
- [dog] l2
- the time}
- line_types(str, 'PLPLP')
- end
-
- def test_groups
- str = "now is the time"
- line_groups(str, ["L0: Paragraph\nnow is the time"] )
-
- str = "now is the time\nfor all good men"
- line_groups(str, ["L0: Paragraph\nnow is the time for all good men"] )
-
- str = %{\
- now is the time
- code _line_ here
- for all good men}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is the time",
- "L0: Verbatim\n code _line_ here\n",
- "L0: Paragraph\nfor all good men"
- ] )
-
- str = "now is the time\n code\n more code\nfor all good men"
- line_groups(str,
- [ "L0: Paragraph\nnow is the time",
- "L0: Verbatim\n code\n more code\n",
- "L0: Paragraph\nfor all good men"
- ] )
-
- str = %{\
- now is
- * l1
- * l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
- str = %{\
- now is
- * l1
- l1+
- * l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1 l1+",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
- str = %{\
- now is
- * l1
- * l1.1
- * l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L2: ListStart\n",
- "L2: ListItem\nl1.1",
- "L2: ListEnd\n",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- * l1
- * l1.1
- text
- code
- code
-
- text
- * l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L2: ListStart\n",
- "L2: ListItem\nl1.1 text",
- "L2: Verbatim\n code\n code\n",
- "L2: Paragraph\ntext",
- "L2: ListEnd\n",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- 1. l1
- * l1.1
- 2. l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L2: ListStart\n",
- "L2: ListItem\nl1.1",
- "L2: ListEnd\n",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
- str = %{\
- now is
- [cat] l1
- * l1.1
- [dog] l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L2: ListStart\n",
- "L2: ListItem\nl1.1",
- "L2: ListEnd\n",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
- str = %{\
- now is
- [cat] l1
- continuation
- [dog] l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1 continuation",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- end
-
- def test_verbatim_merge
- str = %{\
- now is
- code
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- code
- code1
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n code1\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- code
-
- code1
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n\n code1\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- code
-
- code1
-
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n\n code1\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- str = %{\
- now is
- code
-
- code1
-
- code2
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n\n code1\n\n code2\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- # Folds multiple blank lines
- str = %{\
- now is
- code
-
-
- code1
-
- the time}
-
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L0: Verbatim\n code\n\n code1\n",
- "L0: Paragraph\nthe time"
- ])
-
-
- end
-
- def test_list_split
- str = %{\
- now is
- * l1
- 1. n1
- 2. n2
- * l2
- the time}
- line_groups(str,
- [ "L0: Paragraph\nnow is",
- "L1: ListStart\n",
- "L1: ListItem\nl1",
- "L1: ListEnd\n",
- "L1: ListStart\n",
- "L1: ListItem\nn1",
- "L1: ListItem\nn2",
- "L1: ListEnd\n",
- "L1: ListStart\n",
- "L1: ListItem\nl2",
- "L1: ListEnd\n",
- "L0: Paragraph\nthe time"
- ])
-
- end
-
-
- def test_headings
- str = "= heading one"
- line_groups(str,
- [ "L0: Heading\nheading one"
- ])
-
- str = "=== heading three"
- line_groups(str,
- [ "L0: Heading\nheading three"
- ])
-
- str = "text\n === heading three"
- line_groups(str,
- [ "L0: Paragraph\ntext",
- "L0: Verbatim\n === heading three\n"
- ])
-
- str = "text\n code\n === heading three"
- line_groups(str,
- [ "L0: Paragraph\ntext",
- "L0: Verbatim\n code\n === heading three\n"
- ])
-
- str = "text\n code\n=== heading three"
- line_groups(str,
- [ "L0: Paragraph\ntext",
- "L0: Verbatim\n code\n",
- "L0: Heading\nheading three"
- ])
-
- end
-
-
-end
diff --git a/lib/rdoc/markup/text_formatter_test_case.rb b/lib/rdoc/markup/text_formatter_test_case.rb
new file mode 100644
index 0000000000..22a762b5f0
--- /dev/null
+++ b/lib/rdoc/markup/text_formatter_test_case.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+##
+# Test case for creating new plain-text RDoc::Markup formatters. See also
+# RDoc::Markup::FormatterTestCase
+#
+# See test_rdoc_markup_to_rdoc.rb for a complete example.
+#
+# Example:
+#
+# class TestRDocMarkupToNewTextFormat < RDoc::Markup::TextFormatterTestCase
+#
+# add_visitor_tests
+# add_text_tests
+#
+# def setup
+# super
+#
+# @to = RDoc::Markup::ToNewTextFormat.new
+# end
+#
+# def accept_blank_line
+# assert_equal :junk, @to.res.join
+# end
+#
+# # ...
+#
+# end
+
+class RDoc::Markup::TextFormatterTestCase < RDoc::Markup::FormatterTestCase
+
+ ##
+ # Adds test cases to the calling TestCase.
+
+ def self.add_text_tests
+ self.class_eval do
+
+ ##
+ # Test case that calls <tt>@to.accept_heading</tt>
+
+ def test_accept_heading_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ accept_heading_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_rule</tt>
+
+ def test_accept_rule_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_rule @RM::Rule.new(1)
+
+ accept_rule_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_verbatim</tt>
+
+ def test_accept_verbatim_indent
+ @to.start_accepting
+ @to.indent = 2
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
+
+ accept_verbatim_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_verbatim</tt> with a big indent
+
+ def test_accept_verbatim_big_indent
+ @to.start_accepting
+ @to.indent = 2
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", "world\n")
+
+ accept_verbatim_big_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_paragraph</tt> with an indent
+
+ def test_accept_paragraph_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
+
+ accept_paragraph_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_paragraph</tt> with a long line
+
+ def test_accept_paragraph_wrap
+ @to.start_accepting
+ @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
+
+ accept_paragraph_wrap
+ end
+
+ ##
+ # Test case that calls <tt>@to.attributes</tt> with an escaped
+ # cross-reference. If this test doesn't pass something may be very
+ # wrong.
+
+ def test_attributes
+ assert_equal 'Dog', @to.attributes("\\Dog")
+ end
+
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb
new file mode 100644
index 0000000000..6cc3b70e93
--- /dev/null
+++ b/lib/rdoc/markup/to_ansi.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+##
+# Outputs RDoc markup with vibrant ANSI color!
+
+class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
+
+ ##
+ # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color!
+
+ def initialize markup = nil
+ super
+
+ @headings.clear
+ @headings[1] = ["\e[1;32m", "\e[m"] # bold
+ @headings[2] = ["\e[4;32m", "\e[m"] # underline
+ @headings[3] = ["\e[32m", "\e[m"] # just green
+ end
+
+ ##
+ # Maps attributes to ANSI sequences
+
+ def init_tags
+ add_tag :BOLD, "\e[1m", "\e[m"
+ add_tag :TT, "\e[7m", "\e[m"
+ add_tag :EM, "\e[4m", "\e[m"
+ end
+
+ ##
+ # Overrides indent width to ensure output lines up correctly.
+
+ def accept_list_item_end list_item
+ width = case @list_type.last
+ when :BULLET then
+ 2
+ when :NOTE, :LABEL then
+ if @prefix then
+ @res << @prefix.strip
+ @prefix = nil
+ end
+
+ @res << "\n" unless res.length == 1
+ 2
+ else
+ bullet = @list_index.last.to_s
+ @list_index[-1] = @list_index.last.succ
+ bullet.length + 2
+ end
+
+ @indent -= width
+ end
+
+ ##
+ # Adds coloring to note and label list items
+
+ def accept_list_item_start list_item
+ bullet = case @list_type.last
+ when :BULLET then
+ '*'
+ when :NOTE, :LABEL then
+ labels = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ labels << ":\n" unless labels.empty?
+
+ labels
+ else
+ @list_index.last.to_s + '.'
+ end
+
+ case @list_type.last
+ when :NOTE, :LABEL then
+ @indent += 2
+ @prefix = bullet + (' ' * @indent)
+ else
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+
+ width = bullet.gsub(/\e\[[\d;]*m/, '').length + 1
+
+ @indent += width
+ end
+ end
+
+ ##
+ # Starts accepting with a reset screen
+
+ def start_accepting
+ super
+
+ @res = ["\e[0m"]
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
new file mode 100644
index 0000000000..fea017e89d
--- /dev/null
+++ b/lib/rdoc/markup/to_bs.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+##
+# Outputs RDoc markup with hot backspace action! You will probably need a
+# pager to use this output format.
+#
+# This formatter won't work on 1.8.6 because it lacks String#chars.
+
+class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
+
+ ##
+ # Returns a new ToBs that is ready for hot backspace action!
+
+ def initialize markup = nil
+ super
+
+ @in_b = false
+ @in_em = false
+ end
+
+ ##
+ # Sets a flag that is picked up by #annotate to do the right thing in
+ # #convert_string
+
+ def init_tags
+ add_tag :BOLD, '+b', '-b'
+ add_tag :EM, '+_', '-_'
+ add_tag :TT, '' , '' # we need in_tt information maintained
+ end
+
+ ##
+ # Makes heading text bold.
+
+ def accept_heading heading
+ use_prefix or @res << ' ' * @indent
+ @res << @headings[heading.level][0]
+ @in_b = true
+ @res << attributes(heading.text)
+ @in_b = false
+ @res << @headings[heading.level][1]
+ @res << "\n"
+ end
+
+ ##
+ # Turns on or off special handling for +convert_string+
+
+ def annotate tag
+ case tag
+ when '+b' then @in_b = true
+ when '-b' then @in_b = false
+ when '+_' then @in_em = true
+ when '-_' then @in_em = false
+ end
+ ''
+ end
+
+ ##
+ # Calls convert_string on the result of convert_special
+
+ def convert_special special
+ convert_string super
+ end
+
+ ##
+ # Adds bold or underline mixed with backspaces
+
+ def convert_string string
+ return string unless @in_b or @in_em
+ chars = if @in_b then
+ string.chars.map do |char| "#{char}\b#{char}" end
+ elsif @in_em then
+ string.chars.map do |char| "_\b#{char}" end
+ end
+
+ chars.join
+ end
+
+end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
new file mode 100644
index 0000000000..c5e1f073f8
--- /dev/null
+++ b/lib/rdoc/markup/to_html.rb
@@ -0,0 +1,404 @@
+# frozen_string_literal: true
+require 'cgi'
+
+##
+# Outputs RDoc markup as HTML.
+
+class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
+
+ include RDoc::Text
+
+ # :section: Utilities
+
+ ##
+ # Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
+
+ LIST_TYPE_TO_HTML = {
+ :BULLET => ['<ul>', '</ul>'],
+ :LABEL => ['<dl class="rdoc-list label-list">', '</dl>'],
+ :LALPHA => ['<ol style="list-style-type: lower-alpha">', '</ol>'],
+ :NOTE => ['<dl class="rdoc-list note-list">', '</dl>'],
+ :NUMBER => ['<ol>', '</ol>'],
+ :UALPHA => ['<ol style="list-style-type: upper-alpha">', '</ol>'],
+ }
+
+ attr_reader :res # :nodoc:
+ attr_reader :in_list_entry # :nodoc:
+ attr_reader :list # :nodoc:
+
+ ##
+ # The RDoc::CodeObject HTML is being generated for. This is used to
+ # generate namespaced URI fragments
+
+ attr_accessor :code_object
+
+ ##
+ # Path to this document for relative links
+
+ attr_accessor :from_path
+
+ # :section:
+
+ ##
+ # Creates a new formatter that will output HTML
+
+ def initialize options, markup = nil
+ super
+
+ @code_object = nil
+ @from_path = ''
+ @in_list_entry = nil
+ @list = nil
+ @th = nil
+ @hard_break = "<br>\n"
+
+ # external links
+ @markup.add_special(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/,
+ :HYPERLINK)
+
+ add_special_RDOCLINK
+ add_special_TIDYLINK
+
+ init_tags
+ end
+
+ # :section: Special Handling
+ #
+ # These methods handle special markup added by RDoc::Markup#add_special.
+
+ def handle_RDOCLINK url # :nodoc:
+ case url
+ when /^rdoc-ref:/
+ $'
+ when /^rdoc-label:/
+ text = $'
+
+ text = case text
+ when /\Alabel-/ then $'
+ when /\Afootmark-/ then $'
+ when /\Afoottext-/ then $'
+ else text
+ end
+
+ gen_url url, text
+ when /^rdoc-image:/
+ "<img src=\"#{$'}\">"
+ else
+ url =~ /\Ardoc-[a-z]+:/
+
+ $'
+ end
+ end
+
+ ##
+ # +special+ is a <code><br></code>
+
+ def handle_special_HARD_BREAK special
+ '<br>'
+ end
+
+ ##
+ # +special+ is a potential link. The following schemes are handled:
+ #
+ # <tt>mailto:</tt>::
+ # Inserted as-is.
+ # <tt>http:</tt>::
+ # Links are checked to see if they reference an image. If so, that image
+ # gets inserted using an <tt><img></tt> tag. Otherwise a conventional
+ # <tt><a href></tt> is used.
+ # <tt>link:</tt>::
+ # Reference to a local file relative to the output directory.
+
+ def handle_special_HYPERLINK(special)
+ url = special.text
+
+ gen_url url, url
+ end
+
+ ##
+ # +special+ is an rdoc-schemed link that will be converted into a hyperlink.
+ #
+ # For the +rdoc-ref+ scheme the named reference will be returned without
+ # creating a link.
+ #
+ # For the +rdoc-label+ scheme the footnote and label prefixes are stripped
+ # when creating a link. All other contents will be linked verbatim.
+
+ def handle_special_RDOCLINK special
+ handle_RDOCLINK special.text
+ end
+
+ ##
+ # This +special+ is a link where the label is different from the URL
+ # <tt>label[url]</tt> or <tt>{long label}[url]</tt>
+
+ def handle_special_TIDYLINK(special)
+ text = special.text
+
+ return text unless
+ text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
+
+ label = $1
+ url = $2
+
+ label = handle_RDOCLINK label if /^rdoc-image:/ =~ label
+
+ gen_url url, label
+ end
+
+ # :section: Visitor
+ #
+ # These methods implement the HTML visitor.
+
+ ##
+ # Prepares the visitor for HTML generation
+
+ def start_accepting
+ @res = []
+ @in_list_entry = []
+ @list = []
+ end
+
+ ##
+ # Returns the generated output
+
+ def end_accepting
+ @res.join
+ end
+
+ ##
+ # Adds +block_quote+ to the output
+
+ def accept_block_quote block_quote
+ @res << "\n<blockquote>"
+
+ block_quote.parts.each do |part|
+ part.accept self
+ end
+
+ @res << "</blockquote>\n"
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_paragraph paragraph
+ @res << "\n<p>"
+ text = paragraph.text @hard_break
+ text = text.gsub(/\r?\n/, ' ')
+ @res << wrap(to_html(text))
+ @res << "</p>\n"
+ end
+
+ ##
+ # Adds +verbatim+ to the output
+
+ def accept_verbatim verbatim
+ text = verbatim.text.rstrip
+
+ klass = nil
+
+ content = if verbatim.ruby? or parseable? text then
+ begin
+ tokens = RDoc::RipperStateLex.parse text
+ klass = ' class="ruby"'
+
+ result = RDoc::TokenStream.to_html tokens
+ result = result + "\n" unless "\n" == result[-1]
+ result
+ rescue
+ CGI.escapeHTML text
+ end
+ else
+ CGI.escapeHTML text
+ end
+
+ if @options.pipe then
+ @res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
+ else
+ @res << "\n<pre#{klass}>#{content}</pre>\n"
+ end
+ end
+
+ ##
+ # Adds +rule+ to the output
+
+ def accept_rule rule
+ @res << "<hr>\n"
+ end
+
+ ##
+ # Prepares the visitor for consuming +list+
+
+ def accept_list_start(list)
+ @list << list.type
+ @res << html_list_name(list.type, true)
+ @in_list_entry.push false
+ end
+
+ ##
+ # Finishes consumption of +list+
+
+ def accept_list_end(list)
+ @list.pop
+ if tag = @in_list_entry.pop
+ @res << tag
+ end
+ @res << html_list_name(list.type, false) << "\n"
+ end
+
+ ##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start(list_item)
+ if tag = @in_list_entry.last
+ @res << tag
+ end
+
+ @res << list_item_start(list_item, @list.last)
+ end
+
+ ##
+ # Finishes consumption of +list_item+
+
+ def accept_list_item_end(list_item)
+ @in_list_entry[-1] = list_end_for(@list.last)
+ end
+
+ ##
+ # Adds +blank_line+ to the output
+
+ def accept_blank_line(blank_line)
+ # @res << annotate("<p />") << "\n"
+ end
+
+ ##
+ # Adds +heading+ to the output. The headings greater than 6 are trimmed to
+ # level 6.
+
+ def accept_heading heading
+ level = [6, heading.level].min
+
+ label = heading.label @code_object
+
+ @res << if @options.output_decoration
+ "\n<h#{level} id=\"#{label}\">"
+ else
+ "\n<h#{level}>"
+ end
+ @res << to_html(heading.text)
+ unless @options.pipe then
+ @res << "<span><a href=\"##{label}\">&para;</a>"
+ @res << " <a href=\"#top\">&uarr;</a></span>"
+ end
+ @res << "</h#{level}>\n"
+ end
+
+ ##
+ # Adds +raw+ to the output
+
+ def accept_raw raw
+ @res << raw.parts.join("\n")
+ end
+
+ # :section: Utilities
+
+ ##
+ # CGI-escapes +text+
+
+ def convert_string(text)
+ CGI.escapeHTML text
+ end
+
+ ##
+ # Generate a link to +url+ with content +text+. Handles the special cases
+ # for img: and link: described under handle_special_HYPERLINK
+
+ def gen_url url, text
+ scheme, url, id = parse_url url
+
+ if %w[http https link].include?(scheme) and
+ url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
+ "<img src=\"#{url}\" />"
+ else
+ text = text.sub %r%^#{scheme}:/*%i, ''
+ text = text.sub %r%^[*\^](\d+)$%, '\1'
+
+ link = "<a#{id} href=\"#{url}\">#{text}</a>"
+
+ link = "<sup>#{link}</sup>" if /"foot/ =~ id
+
+ link
+ end
+ end
+
+ ##
+ # Determines the HTML list element for +list_type+ and +open_tag+
+
+ def html_list_name(list_type, open_tag)
+ tags = LIST_TYPE_TO_HTML[list_type]
+ raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
+ tags[open_tag ? 0 : 1]
+ end
+
+ ##
+ # Maps attributes to HTML tags
+
+ def init_tags
+ add_tag :BOLD, "<strong>", "</strong>"
+ add_tag :TT, "<code>", "</code>"
+ add_tag :EM, "<em>", "</em>"
+ end
+
+ ##
+ # Returns the HTML tag for +list_type+, possible using a label from
+ # +list_item+
+
+ def list_item_start(list_item, list_type)
+ case list_type
+ when :BULLET, :LALPHA, :NUMBER, :UALPHA then
+ "<li>"
+ when :LABEL, :NOTE then
+ Array(list_item.label).map do |label|
+ "<dt>#{to_html label}\n"
+ end.join << "<dd>"
+ else
+ raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
+ end
+ end
+
+ ##
+ # Returns the HTML end-tag for +list_type+
+
+ def list_end_for(list_type)
+ case list_type
+ when :BULLET, :LALPHA, :NUMBER, :UALPHA then
+ "</li>"
+ when :LABEL, :NOTE then
+ "</dd>"
+ else
+ raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
+ end
+ end
+
+ ##
+ # Returns true if text is valid ruby syntax
+
+ def parseable? text
+ verbose, $VERBOSE = $VERBOSE, nil
+ eval("BEGIN {return true}\n#{text}")
+ rescue SyntaxError
+ false
+ ensure
+ $VERBOSE = verbose
+ end
+
+ ##
+ # Converts +item+ to HTML using RDoc::Text#to_html
+
+ def to_html item
+ super convert_flow @am.flow item
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
new file mode 100644
index 0000000000..2911aee954
--- /dev/null
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+##
+# Subclass of the RDoc::Markup::ToHtml class that supports looking up method
+# names, classes, etc to create links. RDoc::CrossReference is used to
+# generate those links based on the current context.
+
+class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
+
+ # :stopdoc:
+ ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP
+ CLASS_REGEXP_STR = RDoc::CrossReference::CLASS_REGEXP_STR
+ CROSSREF_REGEXP = RDoc::CrossReference::CROSSREF_REGEXP
+ METHOD_REGEXP_STR = RDoc::CrossReference::METHOD_REGEXP_STR
+ # :startdoc:
+
+ ##
+ # RDoc::CodeObject for generating references
+
+ attr_accessor :context
+
+ ##
+ # Should we show '#' characters on method references?
+
+ attr_accessor :show_hash
+
+ ##
+ # Creates a new crossref resolver that generates links relative to +context+
+ # which lives at +from_path+ in the generated files. '#' characters on
+ # references are removed unless +show_hash+ is true. Only method names
+ # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
+
+ def initialize(options, from_path, context, markup = nil)
+ raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
+
+ super options, markup
+
+ @context = context
+ @from_path = from_path
+ @hyperlink_all = @options.hyperlink_all
+ @show_hash = @options.show_hash
+
+ crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
+ @markup.add_special crossref_re, :CROSSREF
+
+ @cross_reference = RDoc::CrossReference.new @context
+ end
+
+ ##
+ # Creates a link to the reference +name+ if the name exists. If +text+ is
+ # given it is used as the link text, otherwise +name+ is used.
+
+ def cross_reference name, text = nil
+ lookup = name
+
+ name = name[1..-1] unless @show_hash if name[0, 1] == '#'
+
+ name = "#{CGI.unescape $'} at #{$1}" if name =~ /(.*[^#:])@/
+
+ text = name unless text
+
+ link lookup, text
+ end
+
+ ##
+ # We're invoked when any text matches the CROSSREF pattern. If we find the
+ # corresponding reference, generate a link. If the name we're looking for
+ # contains no punctuation, we look for it up the module/class chain. For
+ # example, ToHtml is found, even without the <tt>RDoc::Markup::</tt> prefix,
+ # because we look for it in module Markup first.
+
+ def handle_special_CROSSREF(special)
+ name = special.text
+
+ return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
+
+ unless @hyperlink_all then
+ # This ensures that words entirely consisting of lowercase letters will
+ # not have cross-references generated (to suppress lots of erroneous
+ # cross-references to "new" in text, for instance)
+ return name if name =~ /\A[a-z]*\z/
+ end
+
+ cross_reference name
+ end
+
+ ##
+ # Handles <tt>rdoc-ref:</tt> scheme links and allows RDoc::Markup::ToHtml to
+ # handle other schemes.
+
+ def handle_special_HYPERLINK special
+ return cross_reference $' if special.text =~ /\Ardoc-ref:/
+
+ super
+ end
+
+ ##
+ # +special+ is an rdoc-schemed link that will be converted into a hyperlink.
+ # For the rdoc-ref scheme the cross-reference will be looked up and the
+ # given name will be used.
+ #
+ # All other contents are handled by
+ # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_special_RDOCLINK]
+
+ def handle_special_RDOCLINK special
+ url = special.text
+
+ case url
+ when /\Ardoc-ref:/ then
+ cross_reference $'
+ else
+ super
+ end
+ end
+
+ ##
+ # Generates links for <tt>rdoc-ref:</tt> scheme URLs and allows
+ # RDoc::Markup::ToHtml to handle other schemes.
+
+ def gen_url url, text
+ return super unless url =~ /\Ardoc-ref:/
+
+ cross_reference $', text
+ end
+
+ ##
+ # Creates an HTML link to +name+ with the given +text+.
+
+ def link name, text
+ original_name = name
+
+ if name =~ /(.*[^#:])@/ then
+ name = $1
+ label = $'
+ end
+
+ ref = @cross_reference.resolve name, text
+
+ text = ref.output_name @context if
+ RDoc::MethodAttr === ref and text == original_name
+
+ case ref
+ when String then
+ ref
+ else
+ path = ref.as_href @from_path
+
+ if path =~ /#/ then
+ path << "-label-#{label}"
+ elsif ref.sections and
+ ref.sections.any? { |section| label == section.title } then
+ path << "##{label}"
+ else
+ path << "#label-#{label}"
+ end if label
+
+ "<a href=\"#{path}\">#{text}</a>"
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_html_snippet.rb b/lib/rdoc/markup/to_html_snippet.rb
new file mode 100644
index 0000000000..24aa1d32d9
--- /dev/null
+++ b/lib/rdoc/markup/to_html_snippet.rb
@@ -0,0 +1,285 @@
+# frozen_string_literal: true
+##
+# Outputs RDoc markup as paragraphs with inline markup only.
+
+class RDoc::Markup::ToHtmlSnippet < RDoc::Markup::ToHtml
+
+ ##
+ # After this many characters the input will be cut off.
+
+ attr_reader :character_limit
+
+ ##
+ # The number of characters seen so far.
+
+ attr_reader :characters # :nodoc:
+
+ ##
+ # The attribute bitmask
+
+ attr_reader :mask
+
+ ##
+ # After this many paragraphs the input will be cut off.
+
+ attr_reader :paragraph_limit
+
+ ##
+ # Count of paragraphs found
+
+ attr_reader :paragraphs
+
+ ##
+ # Creates a new ToHtmlSnippet formatter that will cut off the input on the
+ # next word boundary after the given number of +characters+ or +paragraphs+
+ # of text have been encountered.
+
+ def initialize options, characters = 100, paragraphs = 3, markup = nil
+ super options, markup
+
+ @character_limit = characters
+ @paragraph_limit = paragraphs
+
+ @characters = 0
+ @mask = 0
+ @paragraphs = 0
+
+ @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ end
+
+ ##
+ # Adds +heading+ to the output as a paragraph
+
+ def accept_heading heading
+ @res << "<p>#{to_html heading.text}\n"
+
+ add_paragraph
+ end
+
+ ##
+ # Raw sections are untrusted and ignored
+
+ alias accept_raw ignore
+
+ ##
+ # Rules are ignored
+
+ alias accept_rule ignore
+
+ def accept_paragraph paragraph
+ para = @in_list_entry.last || "<p>"
+
+ text = paragraph.text @hard_break
+
+ @res << "#{para}#{wrap to_html text}\n"
+
+ add_paragraph
+ end
+
+ ##
+ # Finishes consumption of +list_item+
+
+ def accept_list_item_end list_item
+ end
+
+ ##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start list_item
+ @res << list_item_start(list_item, @list.last)
+ end
+
+ ##
+ # Prepares the visitor for consuming +list+
+
+ def accept_list_start list
+ @list << list.type
+ @res << html_list_name(list.type, true)
+ @in_list_entry.push ''
+ end
+
+ ##
+ # Adds +verbatim+ to the output
+
+ def accept_verbatim verbatim
+ throw :done if @characters >= @character_limit
+ input = verbatim.text.rstrip
+
+ text = truncate input
+ text << ' ...' unless text == input
+
+ super RDoc::Markup::Verbatim.new text
+
+ add_paragraph
+ end
+
+ ##
+ # Prepares the visitor for HTML snippet generation
+
+ def start_accepting
+ super
+
+ @characters = 0
+ end
+
+ ##
+ # Removes escaping from the cross-references in +special+
+
+ def handle_special_CROSSREF special
+ special.text.sub(/\A\\/, '')
+ end
+
+ ##
+ # +special+ is a <code><br></code>
+
+ def handle_special_HARD_BREAK special
+ @characters -= 4
+ '<br>'
+ end
+
+ ##
+ # Lists are paragraphs, but notes and labels have a separator
+
+ def list_item_start list_item, list_type
+ throw :done if @characters >= @character_limit
+
+ case list_type
+ when :BULLET, :LALPHA, :NUMBER, :UALPHA then
+ "<p>"
+ when :LABEL, :NOTE then
+ labels = Array(list_item.label).map do |label|
+ to_html label
+ end.join ', '
+
+ labels << " &mdash; " unless labels.empty?
+
+ start = "<p>#{labels}"
+ @characters += 1 # try to include the label
+ start
+ else
+ raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
+ end
+ end
+
+ ##
+ # Returns just the text of +link+, +url+ is only used to determine the link
+ # type.
+
+ def gen_url url, text
+ if url =~ /^rdoc-label:([^:]*)(?::(.*))?/ then
+ type = "link"
+ elsif url =~ /([A-Za-z]+):(.*)/ then
+ type = $1
+ else
+ type = "http"
+ end
+
+ if (type == "http" or type == "https" or type == "link") and
+ url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
+ ''
+ else
+ text.sub(%r%^#{type}:/*%, '')
+ end
+ end
+
+ ##
+ # In snippets, there are no lists
+
+ def html_list_name list_type, open_tag
+ ''
+ end
+
+ ##
+ # Throws +:done+ when paragraph_limit paragraphs have been encountered
+
+ def add_paragraph
+ @paragraphs += 1
+
+ throw :done if @paragraphs >= @paragraph_limit
+ end
+
+ ##
+ # Marks up +content+
+
+ def convert content
+ catch :done do
+ return super
+ end
+
+ end_accepting
+ end
+
+ ##
+ # Converts flow items +flow+
+
+ def convert_flow flow
+ throw :done if @characters >= @character_limit
+
+ res = []
+ @mask = 0
+
+ flow.each do |item|
+ case item
+ when RDoc::Markup::AttrChanger then
+ off_tags res, item
+ on_tags res, item
+ when String then
+ text = convert_string item
+ res << truncate(text)
+ when RDoc::Markup::Special then
+ text = convert_special item
+ res << truncate(text)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+
+ if @characters >= @character_limit then
+ off_tags res, RDoc::Markup::AttrChanger.new(0, @mask)
+ break
+ end
+ end
+
+ res << ' ...' if @characters >= @character_limit
+
+ res.join
+ end
+
+ ##
+ # Maintains a bitmask to allow HTML elements to be closed properly. See
+ # RDoc::Markup::Formatter.
+
+ def on_tags res, item
+ @mask ^= item.turn_on
+
+ super
+ end
+
+ ##
+ # Maintains a bitmask to allow HTML elements to be closed properly. See
+ # RDoc::Markup::Formatter.
+
+ def off_tags res, item
+ @mask ^= item.turn_off
+
+ super
+ end
+
+ ##
+ # Truncates +text+ at the end of the first word after the character_limit.
+
+ def truncate text
+ length = text.length
+ characters = @characters
+ @characters += length
+
+ return text if @characters < @character_limit
+
+ remaining = @character_limit - characters
+
+ text =~ /\A(.{#{remaining},}?)(\s|$)/m # TODO word-break instead of \s?
+
+ $1
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb
new file mode 100644
index 0000000000..795f3f62ee
--- /dev/null
+++ b/lib/rdoc/markup/to_joined_paragraph.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+##
+# Joins the parts of an RDoc::Markup::Paragraph into a single String.
+#
+# This allows for easier maintenance and testing of Markdown support.
+#
+# This formatter only works on Paragraph instances. Attempting to process
+# other markup syntax items will not work.
+
+class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
+
+ def initialize # :nodoc:
+ super nil
+ end
+
+ def start_accepting # :nodoc:
+ end
+
+ def end_accepting # :nodoc:
+ end
+
+ ##
+ # Converts the parts of +paragraph+ to a single entry.
+
+ def accept_paragraph paragraph
+ parts = paragraph.parts.chunk do |part|
+ String === part
+ end.map do |string, chunk|
+ string ? chunk.join.rstrip : chunk
+ end.flatten
+
+ paragraph.parts.replace parts
+ end
+
+ alias accept_block_quote ignore
+ alias accept_heading ignore
+ alias accept_list_end ignore
+ alias accept_list_item_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_start ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_verbatim ignore
+
+end
+
diff --git a/lib/rdoc/markup/to_label.rb b/lib/rdoc/markup/to_label.rb
new file mode 100644
index 0000000000..9f179013f2
--- /dev/null
+++ b/lib/rdoc/markup/to_label.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+require 'cgi'
+
+##
+# Creates HTML-safe labels suitable for use in id attributes. Tidylinks are
+# converted to their link part and cross-reference links have the suppression
+# marks removed (\\SomeClass is converted to SomeClass).
+
+class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
+
+ attr_reader :res # :nodoc:
+
+ ##
+ # Creates a new formatter that will output HTML-safe labels
+
+ def initialize markup = nil
+ super nil, markup
+
+ @markup.add_special RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK)
+
+ add_tag :BOLD, '', ''
+ add_tag :TT, '', ''
+ add_tag :EM, '', ''
+
+ @res = []
+ end
+
+ ##
+ # Converts +text+ to an HTML-safe label
+
+ def convert text
+ label = convert_flow @am.flow text
+
+ CGI.escape(label).gsub('%', '-').sub(/^-/, '')
+ end
+
+ ##
+ # Converts the CROSSREF +special+ to plain text, removing the suppression
+ # marker, if any
+
+ def handle_special_CROSSREF special
+ text = special.text
+
+ text.sub(/^\\/, '')
+ end
+
+ ##
+ # Converts the TIDYLINK +special+ to just the text part
+
+ def handle_special_TIDYLINK special
+ text = special.text
+
+ return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+
+ $1
+ end
+
+ alias accept_blank_line ignore
+ alias accept_block_quote ignore
+ alias accept_heading ignore
+ alias accept_list_end ignore
+ alias accept_list_item_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_start ignore
+ alias accept_paragraph ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_verbatim ignore
+ alias end_accepting ignore
+ alias handle_special_HARD_BREAK ignore
+ alias start_accepting ignore
+
+end
+
diff --git a/lib/rdoc/markup/to_markdown.rb b/lib/rdoc/markup/to_markdown.rb
new file mode 100644
index 0000000000..d471032f9f
--- /dev/null
+++ b/lib/rdoc/markup/to_markdown.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+# :markup: markdown
+
+##
+# Outputs parsed markup as Markdown
+
+class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
+
+ ##
+ # Creates a new formatter that will output Markdown format text
+
+ def initialize markup = nil
+ super
+
+ @headings[1] = ['# ', '']
+ @headings[2] = ['## ', '']
+ @headings[3] = ['### ', '']
+ @headings[4] = ['#### ', '']
+ @headings[5] = ['##### ', '']
+ @headings[6] = ['###### ', '']
+
+ add_special_RDOCLINK
+ add_special_TIDYLINK
+
+ @hard_break = " \n"
+ end
+
+ ##
+ # Maps attributes to HTML sequences
+
+ def init_tags
+ add_tag :BOLD, '**', '**'
+ add_tag :EM, '*', '*'
+ add_tag :TT, '`', '`'
+ end
+
+ ##
+ # Adds a newline to the output
+
+ def handle_special_HARD_BREAK special
+ " \n"
+ end
+
+ ##
+ # Finishes consumption of `list`
+
+ def accept_list_end list
+ @res << "\n"
+
+ super
+ end
+
+ ##
+ # Finishes consumption of `list_item`
+
+ def accept_list_item_end list_item
+ width = case @list_type.last
+ when :BULLET then
+ 4
+ when :NOTE, :LABEL then
+ use_prefix
+
+ 4
+ else
+ @list_index[-1] = @list_index.last.succ
+ 4
+ end
+
+ @indent -= width
+ end
+
+ ##
+ # Prepares the visitor for consuming `list_item`
+
+ def accept_list_item_start list_item
+ type = @list_type.last
+
+ case type
+ when :NOTE, :LABEL then
+ bullets = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ bullets << "\n:"
+
+ @prefix = ' ' * @indent
+ @indent += 4
+ @prefix << bullets + (' ' * (@indent - 1))
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(4)
+
+ @indent += 4
+ end
+ end
+
+ ##
+ # Prepares the visitor for consuming `list`
+
+ def accept_list_start list
+ case list.type
+ when :BULLET, :LABEL, :NOTE then
+ @list_index << nil
+ when :LALPHA, :NUMBER, :UALPHA then
+ @list_index << 1
+ else
+ raise RDoc::Error, "invalid list type #{list.type}"
+ end
+
+ @list_width << 4
+ @list_type << list.type
+ end
+
+ ##
+ # Adds `rule` to the output
+
+ def accept_rule rule
+ use_prefix or @res << ' ' * @indent
+ @res << '-' * 3
+ @res << "\n"
+ end
+
+ ##
+ # Outputs `verbatim` indented 4 columns
+
+ def accept_verbatim verbatim
+ indent = ' ' * (@indent + 4)
+
+ verbatim.parts.each do |part|
+ @res << indent unless part == "\n"
+ @res << part
+ end
+
+ @res << "\n" unless @res =~ /\n\z/
+ end
+
+ ##
+ # Creates a Markdown-style URL from +url+ with +text+.
+
+ def gen_url url, text
+ scheme, url, = parse_url url
+
+ "[#{text.sub(%r{^#{scheme}:/*}i, '')}](#{url})"
+ end
+
+ ##
+ # Handles <tt>rdoc-</tt> type links for footnotes.
+
+ def handle_rdoc_link url
+ case url
+ when /^rdoc-ref:/ then
+ $'
+ when /^rdoc-label:footmark-(\d+)/ then
+ "[^#{$1}]:"
+ when /^rdoc-label:foottext-(\d+)/ then
+ "[^#{$1}]"
+ when /^rdoc-label:label-/ then
+ gen_url url, $'
+ when /^rdoc-image:/ then
+ "![](#{$'})"
+ when /^rdoc-[a-z]+:/ then
+ $'
+ end
+ end
+
+ ##
+ # Converts the RDoc markup tidylink into a Markdown.style link.
+
+ def handle_special_TIDYLINK special
+ text = special.text
+
+ return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+
+ label = $1
+ url = $2
+
+ if url =~ /^rdoc-label:foot/ then
+ handle_rdoc_link url
+ else
+ gen_url url, label
+ end
+ end
+
+ ##
+ # Converts the rdoc-...: links into a Markdown.style links.
+
+ def handle_special_RDOCLINK special
+ handle_rdoc_link special.text
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
new file mode 100644
index 0000000000..1cb4d6bab2
--- /dev/null
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -0,0 +1,334 @@
+# frozen_string_literal: true
+##
+# Outputs RDoc markup as RDoc markup! (mostly)
+
+class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
+
+ ##
+ # Current indent amount for output in characters
+
+ attr_accessor :indent
+
+ ##
+ # Output width in characters
+
+ attr_accessor :width
+
+ ##
+ # Stack of current list indexes for alphabetic and numeric lists
+
+ attr_reader :list_index
+
+ ##
+ # Stack of list types
+
+ attr_reader :list_type
+
+ ##
+ # Stack of list widths for indentation
+
+ attr_reader :list_width
+
+ ##
+ # Prefix for the next list item. See #use_prefix
+
+ attr_reader :prefix
+
+ ##
+ # Output accumulator
+
+ attr_reader :res
+
+ ##
+ # Creates a new formatter that will output (mostly) \RDoc markup
+
+ def initialize markup = nil
+ super nil, markup
+
+ @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
+ @width = 78
+ init_tags
+
+ @headings = {}
+ @headings.default = []
+
+ @headings[1] = ['= ', '']
+ @headings[2] = ['== ', '']
+ @headings[3] = ['=== ', '']
+ @headings[4] = ['==== ', '']
+ @headings[5] = ['===== ', '']
+ @headings[6] = ['====== ', '']
+
+ @hard_break = "\n"
+ end
+
+ ##
+ # Maps attributes to HTML sequences
+
+ def init_tags
+ add_tag :BOLD, "<b>", "</b>"
+ add_tag :TT, "<tt>", "</tt>"
+ add_tag :EM, "<em>", "</em>"
+ end
+
+ ##
+ # Adds +blank_line+ to the output
+
+ def accept_blank_line blank_line
+ @res << "\n"
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_block_quote block_quote
+ @indent += 2
+
+ block_quote.parts.each do |part|
+ @prefix = '> '
+
+ part.accept self
+ end
+
+ @indent -= 2
+ end
+
+ ##
+ # Adds +heading+ to the output
+
+ def accept_heading heading
+ use_prefix or @res << ' ' * @indent
+ @res << @headings[heading.level][0]
+ @res << attributes(heading.text)
+ @res << @headings[heading.level][1]
+ @res << "\n"
+ end
+
+ ##
+ # Finishes consumption of +list+
+
+ def accept_list_end list
+ @list_index.pop
+ @list_type.pop
+ @list_width.pop
+ end
+
+ ##
+ # Finishes consumption of +list_item+
+
+ def accept_list_item_end list_item
+ width = case @list_type.last
+ when :BULLET then
+ 2
+ when :NOTE, :LABEL then
+ if @prefix then
+ @res << @prefix.strip
+ @prefix = nil
+ end
+
+ @res << "\n"
+ 2
+ else
+ bullet = @list_index.last.to_s
+ @list_index[-1] = @list_index.last.succ
+ bullet.length + 2
+ end
+
+ @indent -= width
+ end
+
+ ##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start list_item
+ type = @list_type.last
+
+ case type
+ when :NOTE, :LABEL then
+ bullets = Array(list_item.label).map do |label|
+ attributes(label).strip
+ end.join "\n"
+
+ bullets << ":\n" unless bullets.empty?
+
+ @prefix = ' ' * @indent
+ @indent += 2
+ @prefix << bullets + (' ' * @indent)
+ else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
+ @prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
+ width = bullet.length + 1
+ @indent += width
+ end
+ end
+
+ ##
+ # Prepares the visitor for consuming +list+
+
+ def accept_list_start list
+ case list.type
+ when :BULLET then
+ @list_index << nil
+ @list_width << 1
+ when :LABEL, :NOTE then
+ @list_index << nil
+ @list_width << 2
+ when :LALPHA then
+ @list_index << 'a'
+ @list_width << list.items.length.to_s.length
+ when :NUMBER then
+ @list_index << 1
+ @list_width << list.items.length.to_s.length
+ when :UALPHA then
+ @list_index << 'A'
+ @list_width << list.items.length.to_s.length
+ else
+ raise RDoc::Error, "invalid list type #{list.type}"
+ end
+
+ @list_type << list.type
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_paragraph paragraph
+ text = paragraph.text @hard_break
+ wrap attributes text
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_indented_paragraph paragraph
+ @indent += paragraph.indent
+ text = paragraph.text @hard_break
+ wrap attributes text
+ @indent -= paragraph.indent
+ end
+
+ ##
+ # Adds +raw+ to the output
+
+ def accept_raw raw
+ @res << raw.parts.join("\n")
+ end
+
+ ##
+ # Adds +rule+ to the output
+
+ def accept_rule rule
+ use_prefix or @res << ' ' * @indent
+ @res << '-' * (@width - @indent)
+ @res << "\n"
+ end
+
+ ##
+ # Outputs +verbatim+ indented 2 columns
+
+ def accept_verbatim verbatim
+ indent = ' ' * (@indent + 2)
+
+ verbatim.parts.each do |part|
+ @res << indent unless part == "\n"
+ @res << part
+ end
+
+ @res << "\n" unless @res =~ /\n\z/
+ end
+
+ ##
+ # Applies attribute-specific markup to +text+ using RDoc::AttributeManager
+
+ def attributes text
+ flow = @am.flow text.dup
+ convert_flow flow
+ end
+
+ ##
+ # Returns the generated output
+
+ def end_accepting
+ @res.join
+ end
+
+ ##
+ # Removes preceding \\ from the suppressed crossref +special+
+
+ def handle_special_SUPPRESSED_CROSSREF special
+ text = special.text
+ text = text.sub('\\', '') unless in_tt?
+ text
+ end
+
+ ##
+ # Adds a newline to the output
+
+ def handle_special_HARD_BREAK special
+ "\n"
+ end
+
+ ##
+ # Prepares the visitor for text generation
+
+ def start_accepting
+ @res = [""]
+ @indent = 0
+ @prefix = nil
+
+ @list_index = []
+ @list_type = []
+ @list_width = []
+ end
+
+ ##
+ # Adds the stored #prefix to the output and clears it. Lists generate a
+ # prefix for later consumption.
+
+ def use_prefix
+ prefix, @prefix = @prefix, nil
+ @res << prefix if prefix
+
+ prefix
+ end
+
+ ##
+ # Wraps +text+ to #width
+
+ def wrap text
+ return unless text && !text.empty?
+
+ text_len = @width - @indent
+
+ text_len = 20 if text_len < 20
+
+ re = /^(.{0,#{text_len}})[ \n]/
+ next_prefix = ' ' * @indent
+
+ prefix = @prefix || next_prefix
+ @prefix = nil
+
+ @res << prefix
+
+ while text.length > text_len
+ if text =~ re then
+ @res << $1
+ text.slice!(0, $&.length)
+ else
+ @res << text.slice!(0, text_len)
+ end
+
+ @res << "\n" << next_prefix
+ end
+
+ if text.empty? then
+ @res.pop
+ @res.pop
+ else
+ @res << text
+ @res << "\n"
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_table_of_contents.rb b/lib/rdoc/markup/to_table_of_contents.rb
new file mode 100644
index 0000000000..f68b90bcf6
--- /dev/null
+++ b/lib/rdoc/markup/to_table_of_contents.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+##
+# Extracts just the RDoc::Markup::Heading elements from a
+# RDoc::Markup::Document to help build a table of contents
+
+class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter
+
+ @to_toc = nil
+
+ ##
+ # Singleton for table-of-contents generation
+
+ def self.to_toc
+ @to_toc ||= new
+ end
+
+ ##
+ # Output accumulator
+
+ attr_reader :res
+
+ ##
+ # Omits headings with a level less than the given level.
+
+ attr_accessor :omit_headings_below
+
+ def initialize # :nodoc:
+ super nil
+
+ @omit_headings_below = nil
+ end
+
+ ##
+ # Adds +document+ to the output, using its heading cutoff if present
+
+ def accept_document document
+ @omit_headings_below = document.omit_headings_below
+
+ super
+ end
+
+ ##
+ # Adds +heading+ to the table of contents
+
+ def accept_heading heading
+ @res << heading unless suppressed? heading
+ end
+
+ ##
+ # Returns the table of contents
+
+ def end_accepting
+ @res
+ end
+
+ ##
+ # Prepares the visitor for text generation
+
+ def start_accepting
+ @omit_headings_below = nil
+ @res = []
+ end
+
+ ##
+ # Returns true if +heading+ is below the display threshold
+
+ def suppressed? heading
+ return false unless @omit_headings_below
+
+ heading.level > @omit_headings_below
+ end
+
+ # :stopdoc:
+ alias accept_block_quote ignore
+ alias accept_raw ignore
+ alias accept_rule ignore
+ alias accept_blank_line ignore
+ alias accept_paragraph ignore
+ alias accept_verbatim ignore
+ alias accept_list_end ignore
+ alias accept_list_item_start ignore
+ alias accept_list_item_end ignore
+ alias accept_list_end_bullet ignore
+ alias accept_list_start ignore
+ # :startdoc:
+
+end
+
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
new file mode 100644
index 0000000000..61d3cffaf0
--- /dev/null
+++ b/lib/rdoc/markup/to_test.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+##
+# This Markup outputter is used for testing purposes.
+
+class RDoc::Markup::ToTest < RDoc::Markup::Formatter
+
+ # :stopdoc:
+
+ ##
+ # :section: Visitor
+
+ def start_accepting
+ @res = []
+ @list = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(paragraph)
+ @res << convert_flow(@am.flow(paragraph.text))
+ end
+
+ def accept_raw raw
+ @res << raw.parts.join
+ end
+
+ def accept_verbatim(verbatim)
+ @res << verbatim.text.gsub(/^(\S)/, ' \1')
+ end
+
+ def accept_list_start(list)
+ @list << case list.type
+ when :BULLET then
+ '*'
+ when :NUMBER then
+ '1'
+ else
+ list.type
+ end
+ end
+
+ def accept_list_end(list)
+ @list.pop
+ end
+
+ def accept_list_item_start(list_item)
+ @res << "#{' ' * (@list.size - 1)}#{@list.last}: "
+ end
+
+ def accept_list_item_end(list_item)
+ end
+
+ def accept_blank_line(blank_line)
+ @res << "\n"
+ end
+
+ def accept_heading(heading)
+ @res << "#{'=' * heading.level} #{heading.text}"
+ end
+
+ def accept_rule(rule)
+ @res << '-' * rule.weight
+ end
+
+ # :startdoc:
+
+end
+
diff --git a/lib/rdoc/markup/to_tt_only.rb b/lib/rdoc/markup/to_tt_only.rb
new file mode 100644
index 0000000000..4f43546e3d
--- /dev/null
+++ b/lib/rdoc/markup/to_tt_only.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+##
+# Extracts sections of text enclosed in plus, tt or code. Used to discover
+# undocumented parameters.
+
+class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
+
+ ##
+ # Stack of list types
+
+ attr_reader :list_type
+
+ ##
+ # Output accumulator
+
+ attr_reader :res
+
+ ##
+ # Creates a new tt-only formatter.
+
+ def initialize markup = nil
+ super nil, markup
+
+ add_tag :TT, nil, nil
+ end
+
+ ##
+ # Adds tts from +block_quote+ to the output
+
+ def accept_block_quote block_quote
+ tt_sections block_quote.text
+ end
+
+ ##
+ # Pops the list type for +list+ from #list_type
+
+ def accept_list_end list
+ @list_type.pop
+ end
+
+ ##
+ # Pushes the list type for +list+ onto #list_type
+
+ def accept_list_start list
+ @list_type << list.type
+ end
+
+ ##
+ # Prepares the visitor for consuming +list_item+
+
+ def accept_list_item_start list_item
+ case @list_type.last
+ when :NOTE, :LABEL then
+ Array(list_item.label).map do |label|
+ tt_sections label
+ end.flatten
+ end
+ end
+
+ ##
+ # Adds +paragraph+ to the output
+
+ def accept_paragraph paragraph
+ tt_sections(paragraph.text)
+ end
+
+ ##
+ # Does nothing to +markup_item+ because it doesn't have any user-built
+ # content
+
+ def do_nothing markup_item
+ end
+
+ alias accept_blank_line do_nothing # :nodoc:
+ alias accept_heading do_nothing # :nodoc:
+ alias accept_list_item_end do_nothing # :nodoc:
+ alias accept_raw do_nothing # :nodoc:
+ alias accept_rule do_nothing # :nodoc:
+ alias accept_verbatim do_nothing # :nodoc:
+
+ ##
+ # Extracts tt sections from +text+
+
+ def tt_sections text
+ flow = @am.flow text.dup
+
+ flow.each do |item|
+ case item
+ when String then
+ @res << item if in_tt?
+ when RDoc::Markup::AttrChanger then
+ off_tags res, item
+ on_tags res, item
+ when RDoc::Markup::Special then
+ @res << convert_special(item) if in_tt? # TODO can this happen?
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+
+ res
+ end
+
+ ##
+ # Returns an Array of items that were wrapped in plus, tt or code.
+
+ def end_accepting
+ @res.compact
+ end
+
+ ##
+ # Prepares the visitor for gathering tt sections
+
+ def start_accepting
+ @res = []
+
+ @list_type = []
+ end
+
+end
+
diff --git a/lib/rdoc/markup/verbatim.rb b/lib/rdoc/markup/verbatim.rb
new file mode 100644
index 0000000000..7f1bc29a09
--- /dev/null
+++ b/lib/rdoc/markup/verbatim.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+##
+# A section of verbatim text
+
+class RDoc::Markup::Verbatim < RDoc::Markup::Raw
+
+ ##
+ # Format of this verbatim section
+
+ attr_accessor :format
+
+ def initialize *parts # :nodoc:
+ super
+
+ @format = nil
+ end
+
+ def == other # :nodoc:
+ super and @format == other.format
+ end
+
+ ##
+ # Calls #accept_verbatim on +visitor+
+
+ def accept visitor
+ visitor.accept_verbatim self
+ end
+
+ ##
+ # Collapses 3+ newlines into two newlines
+
+ def normalize
+ parts = []
+
+ newlines = 0
+
+ @parts.each do |part|
+ case part
+ when /^\s*\n/ then
+ newlines += 1
+ parts << part if newlines == 1
+ else
+ newlines = 0
+ parts << part
+ end
+ end
+
+ parts.pop if parts.last =~ /\A\r?\n\z/
+
+ @parts = parts
+ end
+
+ def pretty_print q # :nodoc:
+ self.class.name =~ /.*::(\w{1,4})/i
+
+ q.group 2, "[#{$1.downcase}: ", ']' do
+ if @format then
+ q.text "format: #{@format}"
+ q.breakable
+ end
+
+ q.seplist @parts do |part|
+ q.pp part
+ end
+ end
+ end
+
+ ##
+ # Is this verbatim section Ruby code?
+
+ def ruby?
+ @format ||= nil # TODO for older ri data, switch the tree to marshal_dump
+ @format == :ruby
+ end
+
+ ##
+ # The text of the section
+
+ def text
+ @parts.join
+ end
+
+end
+
diff --git a/lib/rdoc/meta_method.rb b/lib/rdoc/meta_method.rb
new file mode 100644
index 0000000000..7927a9ce9c
--- /dev/null
+++ b/lib/rdoc/meta_method.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+##
+# MetaMethod represents a meta-programmed method
+
+class RDoc::MetaMethod < RDoc::AnyMethod
+end
+
diff --git a/lib/rdoc/method_attr.rb b/lib/rdoc/method_attr.rb
new file mode 100644
index 0000000000..3cef78c4a5
--- /dev/null
+++ b/lib/rdoc/method_attr.rb
@@ -0,0 +1,419 @@
+# frozen_string_literal: true
+##
+# Abstract class representing either a method or an attribute.
+
+class RDoc::MethodAttr < RDoc::CodeObject
+
+ include Comparable
+
+ ##
+ # Name of this method/attribute.
+
+ attr_accessor :name
+
+ ##
+ # public, protected, private
+
+ attr_accessor :visibility
+
+ ##
+ # Is this a singleton method/attribute?
+
+ attr_accessor :singleton
+
+ ##
+ # Source file token stream
+
+ attr_reader :text
+
+ ##
+ # Array of other names for this method/attribute
+
+ attr_reader :aliases
+
+ ##
+ # The method/attribute we're aliasing
+
+ attr_accessor :is_alias_for
+
+ #--
+ # The attributes below are for AnyMethod only.
+ # They are left here for the time being to
+ # allow ri to operate.
+ # TODO modify ri to avoid calling these on attributes.
+ #++
+
+ ##
+ # Parameters yielded by the called block
+
+ attr_reader :block_params
+
+ ##
+ # Parameters for this method
+
+ attr_accessor :params
+
+ ##
+ # Different ways to call this method
+
+ attr_accessor :call_seq
+
+ ##
+ # The call_seq or the param_seq with method name, if there is no call_seq.
+
+ attr_reader :arglists
+
+ ##
+ # Pretty parameter list for this method
+
+ attr_reader :param_seq
+
+
+ ##
+ # Creates a new MethodAttr from token stream +text+ and method or attribute
+ # name +name+.
+ #
+ # Usually this is called by super from a subclass.
+
+ def initialize text, name
+ super()
+
+ @text = text
+ @name = name
+
+ @aliases = []
+ @is_alias_for = nil
+ @parent_name = nil
+ @singleton = nil
+ @visibility = :public
+ @see = false
+
+ @arglists = nil
+ @block_params = nil
+ @call_seq = nil
+ @param_seq = nil
+ @params = nil
+ end
+
+ ##
+ # Resets cached data for the object so it can be rebuilt by accessor methods
+
+ def initialize_copy other # :nodoc:
+ @full_name = nil
+ end
+
+ def initialize_visibility # :nodoc:
+ super
+ @see = nil
+ end
+
+ ##
+ # Order by #singleton then #name
+
+ def <=>(other)
+ return unless other.respond_to?(:singleton) &&
+ other.respond_to?(:name)
+
+ [ @singleton ? 0 : 1, name] <=>
+ [other.singleton ? 0 : 1, other.name]
+ end
+
+ def == other # :nodoc:
+ equal?(other) or self.class == other.class and full_name == other.full_name
+ end
+
+ ##
+ # A method/attribute is documented if any of the following is true:
+ # - it was marked with :nodoc:;
+ # - it has a comment;
+ # - it is an alias for a documented method;
+ # - it has a +#see+ method that is documented.
+
+ def documented?
+ super or
+ (is_alias_for and is_alias_for.documented?) or
+ (see and see.documented?)
+ end
+
+ ##
+ # A method/attribute to look at,
+ # in particular if this method/attribute has no documentation.
+ #
+ # It can be a method/attribute of the superclass or of an included module,
+ # including the Kernel module, which is always appended to the included
+ # modules.
+ #
+ # Returns +nil+ if there is no such method/attribute.
+ # The +#is_alias_for+ method/attribute, if any, is not included.
+ #
+ # Templates may generate a "see also ..." if this method/attribute
+ # has documentation, and "see ..." if it does not.
+
+ def see
+ @see = find_see if @see == false
+ @see
+ end
+
+ ##
+ # Sets the store for this class or module and its contained code objects.
+
+ def store= store
+ super
+
+ @file = @store.add_file @file.full_name if @file
+ end
+
+ def find_see # :nodoc:
+ return nil if singleton || is_alias_for
+
+ # look for the method
+ other = find_method_or_attribute name
+ return other if other
+
+ # if it is a setter, look for a getter
+ return nil unless name =~ /[a-z_]=$/i # avoid == or ===
+ return find_method_or_attribute name[0..-2]
+ end
+
+ def find_method_or_attribute name # :nodoc:
+ return nil unless parent.respond_to? :ancestors
+
+ searched = parent.ancestors
+ kernel = @store.modules_hash['Kernel']
+
+ searched << kernel if kernel &&
+ parent != kernel && !searched.include?(kernel)
+
+ searched.each do |ancestor|
+ next if String === ancestor
+ next if parent == ancestor
+
+ other = ancestor.find_method_named('#' + name) ||
+ ancestor.find_attribute_named(name)
+
+ return other if other
+ end
+
+ nil
+ end
+
+ ##
+ # Abstract method. Contexts in their building phase call this
+ # to register a new alias for this known method/attribute.
+ #
+ # - creates a new AnyMethod/Attribute named <tt>an_alias.new_name</tt>;
+ # - adds +self+ as an alias for the new method or attribute
+ # - adds the method or attribute to #aliases
+ # - adds the method or attribute to +context+.
+
+ def add_alias(an_alias, context)
+ raise NotImplementedError
+ end
+
+ ##
+ # HTML fragment reference for this method
+
+ def aref
+ type = singleton ? 'c' : 'i'
+ # % characters are not allowed in html names => dash instead
+ "#{aref_prefix}-#{type}-#{html_name}"
+ end
+
+ ##
+ # Prefix for +aref+, defined by subclasses.
+
+ def aref_prefix
+ raise NotImplementedError
+ end
+
+ ##
+ # Attempts to sanitize the content passed by the Ruby parser:
+ # remove outer parentheses, etc.
+
+ def block_params=(value)
+ # 'yield.to_s' or 'assert yield, msg'
+ return @block_params = '' if value =~ /^[\.,]/
+
+ # remove trailing 'if/unless ...'
+ return @block_params = '' if value =~ /^(if|unless)\s/
+
+ value = $1.strip if value =~ /^(.+)\s(if|unless)\s/
+
+ # outer parentheses
+ value = $1 if value =~ /^\s*\((.*)\)\s*$/
+ value = value.strip
+
+ # proc/lambda
+ return @block_params = $1 if value =~ /^(proc|lambda)(\s*\{|\sdo)/
+
+ # surrounding +...+ or [...]
+ value = $1.strip if value =~ /^\+(.*)\+$/
+ value = $1.strip if value =~ /^\[(.*)\]$/
+
+ return @block_params = '' if value.empty?
+
+ # global variable
+ return @block_params = 'str' if value =~ /^\$[&0-9]$/
+
+ # wipe out array/hash indices
+ value.gsub!(/(\w)\[[^\[]+\]/, '\1')
+
+ # remove @ from class/instance variables
+ value.gsub!(/@@?([a-z0-9_]+)/, '\1')
+
+ # method calls => method name
+ value.gsub!(/([A-Z:a-z0-9_]+)\.([a-z0-9_]+)(\s*\(\s*[a-z0-9_.,\s]*\s*\)\s*)?/) do
+ case $2
+ when 'to_s' then $1
+ when 'const_get' then 'const'
+ when 'new' then
+ $1.split('::').last. # ClassName => class_name
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ downcase
+ else
+ $2
+ end
+ end
+
+ # class prefixes
+ value.gsub!(/[A-Za-z0-9_:]+::/, '')
+
+ # simple expressions
+ value = $1 if value =~ /^([a-z0-9_]+)\s*[-*+\/]/
+
+ @block_params = value.strip
+ end
+
+ ##
+ # HTML id-friendly method/attribute name
+
+ def html_name
+ require 'cgi'
+
+ CGI.escape(@name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '')
+ end
+
+ ##
+ # Full method/attribute name including namespace
+
+ def full_name
+ @full_name ||= "#{parent_name}#{pretty_name}"
+ end
+
+ def inspect # :nodoc:
+ alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
+ visibility = self.visibility
+ visibility = "forced #{visibility}" if force_documentation
+ "#<%s:0x%x %s (%s)%s>" % [
+ self.class, object_id,
+ full_name,
+ visibility,
+ alias_for,
+ ]
+ end
+
+ ##
+ # '::' for a class method/attribute, '#' for an instance method.
+
+ def name_prefix
+ @singleton ? '::' : '#'
+ end
+
+ ##
+ # Name for output to HTML. For class methods the full name with a "." is
+ # used like +SomeClass.method_name+. For instance methods the class name is
+ # used if +context+ does not match the parent.
+ #
+ # This is to help prevent people from using :: to call class methods.
+
+ def output_name context
+ return "#{name_prefix}#{@name}" if context == parent
+
+ "#{parent_name}#{@singleton ? '.' : '#'}#{@name}"
+ end
+
+ ##
+ # Method/attribute name with class/instance indicator
+
+ def pretty_name
+ "#{name_prefix}#{@name}"
+ end
+
+ ##
+ # Type of method/attribute (class or instance)
+
+ def type
+ singleton ? 'class' : 'instance'
+ end
+
+ ##
+ # Path to this method for use with HTML generator output.
+
+ def path
+ "#{@parent.path}##{aref}"
+ end
+
+ ##
+ # Name of our parent with special handling for un-marshaled methods
+
+ def parent_name
+ @parent_name || super
+ end
+
+ def pretty_print q # :nodoc:
+ alias_for =
+ if @is_alias_for.respond_to? :name then
+ "alias for #{@is_alias_for.name}"
+ elsif Array === @is_alias_for then
+ "alias for #{@is_alias_for.last}"
+ end
+
+ q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
+ if alias_for then
+ q.breakable
+ q.text alias_for
+ end
+
+ if text then
+ q.breakable
+ q.text "text:"
+ q.breakable
+ q.pp @text
+ end
+
+ unless comment.empty? then
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
+ end
+ end
+
+ ##
+ # Used by RDoc::Generator::JsonIndex to create a record for the search
+ # engine.
+
+ def search_record
+ [
+ @name,
+ full_name,
+ @name,
+ @parent.full_name,
+ path,
+ params,
+ snippet(@comment),
+ ]
+ end
+
+ def to_s # :nodoc:
+ if @is_alias_for
+ "#{self.class.name}: #{full_name} -> #{is_alias_for}"
+ else
+ "#{self.class.name}: #{full_name}"
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/mixin.rb b/lib/rdoc/mixin.rb
new file mode 100644
index 0000000000..379d7cc526
--- /dev/null
+++ b/lib/rdoc/mixin.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+##
+# A Mixin adds features from a module into another context. RDoc::Include and
+# RDoc::Extend are both mixins.
+
+class RDoc::Mixin < RDoc::CodeObject
+
+ ##
+ # Name of included module
+
+ attr_accessor :name
+
+ ##
+ # Creates a new Mixin for +name+ with +comment+
+
+ def initialize(name, comment)
+ super()
+ @name = name
+ self.comment = comment
+ @module = nil # cache for module if found
+ end
+
+ ##
+ # Mixins are sorted by name
+
+ def <=> other
+ return unless self.class === other
+
+ name <=> other.name
+ end
+
+ def == other # :nodoc:
+ self.class === other and @name == other.name
+ end
+
+ alias eql? == # :nodoc:
+
+ ##
+ # Full name based on #module
+
+ def full_name
+ m = self.module
+ RDoc::ClassModule === m ? m.full_name : @name
+ end
+
+ def hash # :nodoc:
+ [@name, self.module].hash
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %s.%s %s>" % [
+ self.class,
+ object_id,
+ parent_name, self.class.name.downcase, @name,
+ ]
+ end
+
+ ##
+ # Attempts to locate the included module object. Returns the name if not
+ # known.
+ #
+ # The scoping rules of Ruby to resolve the name of an included module are:
+ # - first look into the children of the current context;
+ # - if not found, look into the children of included modules,
+ # in reverse inclusion order;
+ # - if still not found, go up the hierarchy of names.
+ #
+ # This method has <code>O(n!)</code> behavior when the module calling
+ # include is referencing nonexistent modules. Avoid calling #module until
+ # after all the files are parsed. This behavior is due to ruby's constant
+ # lookup behavior.
+ #
+ # As of the beginning of October, 2011, no gem includes nonexistent modules.
+
+ def module
+ return @module if @module
+
+ # search the current context
+ return @name unless parent
+ full_name = parent.child_name(@name)
+ @module = @store.modules_hash[full_name]
+ return @module if @module
+ return @name if @name =~ /^::/
+
+ # search the includes before this one, in reverse order
+ searched = parent.includes.take_while { |i| i != self }.reverse
+ searched.each do |i|
+ inc = i.module
+ next if String === inc
+ full_name = inc.child_name(@name)
+ @module = @store.modules_hash[full_name]
+ return @module if @module
+ end
+
+ # go up the hierarchy of names
+ up = parent.parent
+ while up
+ full_name = up.child_name(@name)
+ @module = @store.modules_hash[full_name]
+ return @module if @module
+ up = up.parent
+ end
+
+ @name
+ end
+
+ ##
+ # Sets the store for this class or module and its contained code objects.
+
+ def store= store
+ super
+
+ @file = @store.add_file @file.full_name if @file
+ end
+
+ def to_s # :nodoc:
+ "#{self.class.name.downcase} #@name in: #{parent}"
+ end
+
+end
+
diff --git a/lib/rdoc/normal_class.rb b/lib/rdoc/normal_class.rb
new file mode 100644
index 0000000000..6729b18448
--- /dev/null
+++ b/lib/rdoc/normal_class.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+##
+# A normal class, neither singleton nor anonymous
+
+class RDoc::NormalClass < RDoc::ClassModule
+
+ ##
+ # The ancestors of this class including modules. Unlike Module#ancestors,
+ # this class is not included in the result. The result will contain both
+ # RDoc::ClassModules and Strings.
+
+ def ancestors
+ if String === superclass then
+ super << superclass
+ elsif superclass then
+ ancestors = super
+ ancestors << superclass
+ ancestors.concat superclass.ancestors
+ else
+ super
+ end
+ end
+
+ def aref_prefix # :nodoc:
+ 'class'
+ end
+
+ ##
+ # The definition of this class, <tt>class MyClassName</tt>
+
+ def definition
+ "class #{full_name}"
+ end
+
+ def direct_ancestors
+ superclass ? super + [superclass] : super
+ end
+
+ def inspect # :nodoc:
+ superclass = @superclass ? " < #{@superclass}" : nil
+ "<%s:0x%x class %s%s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ full_name, superclass, @includes, @extends, @attributes, @method_list, @aliases
+ ]
+ end
+
+ def to_s # :nodoc:
+ display = "#{self.class.name} #{self.full_name}"
+ if superclass
+ display += ' < ' + (superclass.is_a?(String) ? superclass : superclass.full_name)
+ end
+ display += ' -> ' + is_alias_for.to_s if is_alias_for
+ display
+ end
+
+ def pretty_print q # :nodoc:
+ superclass = @superclass ? " < #{@superclass}" : nil
+
+ q.group 2, "[class #{full_name}#{superclass} ", "]" do
+ q.breakable
+ q.text "includes:"
+ q.breakable
+ q.seplist @includes do |inc| q.pp inc end
+
+ q.breakable
+ q.text "constants:"
+ q.breakable
+ q.seplist @constants do |const| q.pp const end
+
+ q.breakable
+ q.text "attributes:"
+ q.breakable
+ q.seplist @attributes do |attr| q.pp attr end
+
+ q.breakable
+ q.text "methods:"
+ q.breakable
+ q.seplist @method_list do |meth| q.pp meth end
+
+ q.breakable
+ q.text "aliases:"
+ q.breakable
+ q.seplist @aliases do |aliaz| q.pp aliaz end
+
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp comment
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/normal_module.rb b/lib/rdoc/normal_module.rb
new file mode 100644
index 0000000000..8f364be41c
--- /dev/null
+++ b/lib/rdoc/normal_module.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+##
+# A normal module, like NormalClass
+
+class RDoc::NormalModule < RDoc::ClassModule
+
+ def aref_prefix # :nodoc:
+ 'module'
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x module %s includes: %p extends: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ full_name, @includes, @extends, @attributes, @method_list, @aliases
+ ]
+ end
+
+ ##
+ # The definition of this module, <tt>module MyModuleName</tt>
+
+ def definition
+ "module #{full_name}"
+ end
+
+ ##
+ # This is a module, returns true
+
+ def module?
+ true
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[module #{full_name}: ", "]" do
+ q.breakable
+ q.text "includes:"
+ q.breakable
+ q.seplist @includes do |inc| q.pp inc end
+ q.breakable
+
+ q.breakable
+ q.text "constants:"
+ q.breakable
+ q.seplist @constants do |const| q.pp const end
+
+ q.text "attributes:"
+ q.breakable
+ q.seplist @attributes do |attr| q.pp attr end
+ q.breakable
+
+ q.text "methods:"
+ q.breakable
+ q.seplist @method_list do |meth| q.pp meth end
+ q.breakable
+
+ q.text "aliases:"
+ q.breakable
+ q.seplist @aliases do |aliaz| q.pp aliaz end
+ q.breakable
+
+ q.text "comment:"
+ q.breakable
+ q.pp comment
+ end
+ end
+
+ ##
+ # Modules don't have one, raises NoMethodError
+
+ def superclass
+ raise NoMethodError, "#{full_name} is a module"
+ end
+
+end
+
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 31441793c4..17bbca81fe 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -1,575 +1,1227 @@
-# We handle the parsing of options, and subsequently as a singleton
-# object to be queried for option values
+# frozen_string_literal: true
+require 'optparse'
+require 'pathname'
+
+##
+# RDoc::Options handles the parsing and storage of options
+#
+# == Saved Options
+#
+# You can save some options like the markup format in the
+# <tt>.rdoc_options</tt> file in your gem. The easiest way to do this is:
+#
+# rdoc --markup tomdoc --write-options
+#
+# Which will automatically create the file and fill it with the options you
+# specified.
+#
+# The following options will not be saved since they interfere with the user's
+# preferences or with the normal operation of RDoc:
+#
+# * +--coverage-report+
+# * +--dry-run+
+# * +--encoding+
+# * +--force-update+
+# * +--format+
+# * +--pipe+
+# * +--quiet+
+# * +--template+
+# * +--verbose+
+#
+# == Custom Options
+#
+# Generators can hook into RDoc::Options to add generator-specific command
+# line options.
+#
+# When <tt>--format</tt> is encountered in ARGV, RDoc calls ::setup_options on
+# the generator class to add extra options to the option parser. Options for
+# custom generators must occur after <tt>--format</tt>. <tt>rdoc --help</tt>
+# will list options for all installed generators.
+#
+# Example:
+#
+# class RDoc::Generator::Spellcheck
+# RDoc::RDoc.add_generator self
+#
+# def self.setup_options rdoc_options
+# op = rdoc_options.option_parser
+#
+# op.on('--spell-dictionary DICTIONARY',
+# RDoc::Options::Path) do |dictionary|
+# rdoc_options.spell_dictionary = dictionary
+# end
+# end
+# end
+#
+# Of course, RDoc::Options does not respond to +spell_dictionary+ by default
+# so you will need to add it:
+#
+# class RDoc::Options
+#
+# ##
+# # The spell dictionary used by the spell-checking plugin.
+#
+# attr_accessor :spell_dictionary
+#
+# end
+#
+# == Option Validators
+#
+# OptionParser validators will validate and cast user input values. In
+# addition to the validators that ship with OptionParser (String, Integer,
+# Float, TrueClass, FalseClass, Array, Regexp, Date, Time, URI, etc.),
+# RDoc::Options adds Path, PathArray and Template.
+
+class RDoc::Options
+
+ ##
+ # The deprecated options.
+
+ DEPRECATED = {
+ '--accessor' => 'support discontinued',
+ '--diagram' => 'support discontinued',
+ '--help-output' => 'support discontinued',
+ '--image-format' => 'was an option for --diagram',
+ '--inline-source' => 'source code is now always inlined',
+ '--merge' => 'ri now always merges class information',
+ '--one-file' => 'support discontinued',
+ '--op-name' => 'support discontinued',
+ '--opname' => 'support discontinued',
+ '--promiscuous' => 'files always only document their content',
+ '--ri-system' => 'Ruby installers use other techniques',
+ }
+
+ ##
+ # RDoc options ignored (or handled specially) by --write-options
+
+ SPECIAL = %w[
+ coverage_report
+ dry_run
+ encoding
+ files
+ force_output
+ force_update
+ generator
+ generator_name
+ generator_options
+ generators
+ op_dir
+ option_parser
+ pipe
+ rdoc_include
+ root
+ static_path
+ stylesheet_url
+ template
+ template_dir
+ update_output_dir
+ verbosity
+ write_options
+ ]
+
+ ##
+ # Option validator for OptionParser that matches a directory that exists on
+ # the filesystem.
+
+ Directory = Object.new
+
+ ##
+ # Option validator for OptionParser that matches a file or directory that
+ # exists on the filesystem.
+
+ Path = Object.new
+
+ ##
+ # Option validator for OptionParser that matches a comma-separated list of
+ # files or directories that exist on the filesystem.
+
+ PathArray = Object.new
+
+ ##
+ # Option validator for OptionParser that matches a template directory for an
+ # installed generator that lives in
+ # <tt>"rdoc/generator/template/#{template_name}"</tt>
+
+ Template = Object.new
+
+ ##
+ # Character-set for HTML output. #encoding is preferred over #charset
+
+ attr_accessor :charset
+
+ ##
+ # If true, RDoc will not write any files.
+
+ attr_accessor :dry_run
+
+ ##
+ # The output encoding. All input files will be transcoded to this encoding.
+ #
+ # The default encoding is UTF-8. This is set via --encoding.
+
+ attr_accessor :encoding
+
+ ##
+ # Files matching this pattern will be excluded
-require "rdoc/ri/ri_paths"
+ attr_accessor :exclude
-class Options
+ ##
+ # The list of files to be processed
- require 'singleton'
- require 'getoptlong'
+ attr_accessor :files
- include Singleton
+ ##
+ # Create the output even if the output directory does not look
+ # like an rdoc output directory
- # files matching this pattern will be excluded
- attr_accessor :exclude
+ attr_accessor :force_output
- # the name of the output directory
- attr_accessor :op_dir
-
- # the name to use for the output
- attr_reader :op_name
-
- # include private and protected methods in the
- # output
- attr_accessor :show_all
-
- # name of the file, class or module to display in
- # the initial index page (if not specified
- # the first file we encounter is used)
- attr_accessor :main_page
+ ##
+ # Scan newer sources than the flag file if true.
- # merge into classes of the name name when generating ri
- attr_reader :merge
+ attr_accessor :force_update
- # Don't display progress as we process the files
- attr_reader :quiet
+ ##
+ # Formatter to mark up text with
+
+ attr_accessor :formatter
+
+ ##
+ # Description of the output generator (set with the <tt>--format</tt> option)
- # description of the output generator (set with the <tt>-fmt</tt>
- # option
attr_accessor :generator
- # and the list of files to be processed
- attr_reader :files
+ ##
+ # For #==
+
+ attr_reader :generator_name # :nodoc:
+
+ ##
+ # Loaded generator options. Used to prevent --help from loading the same
+ # options multiple times.
+
+ attr_accessor :generator_options
+
+ ##
+ # Old rdoc behavior: hyperlink all words that match a method name,
+ # even if not preceded by '#' or '::'
+
+ attr_accessor :hyperlink_all
+
+ ##
+ # Include line numbers in the source code
+
+ attr_accessor :line_numbers
+
+ ##
+ # The output locale.
+
+ attr_accessor :locale
+
+ ##
+ # The directory where locale data live.
+
+ attr_accessor :locale_dir
+
+ ##
+ # Name of the file, class or module to display in the initial index page (if
+ # not specified the first file we encounter is used)
+
+ attr_accessor :main_page
+
+ ##
+ # The default markup format. The default is 'rdoc'. 'markdown', 'tomdoc'
+ # and 'rd' are also built-in.
+
+ attr_accessor :markup
+
+ ##
+ # If true, only report on undocumented files
+
+ attr_accessor :coverage_report
+
+ ##
+ # The name of the output directory
+
+ attr_accessor :op_dir
+
+ ##
+ # The OptionParser for this instance
+
+ attr_accessor :option_parser
+
+ ##
+ # Output heading decorations?
+ attr_accessor :output_decoration
+
+ ##
+ # Directory where guides, FAQ, and other pages not associated with a class
+ # live. You may leave this unset if these are at the root of your project.
+
+ attr_accessor :page_dir
+
+ ##
+ # Is RDoc in pipe mode?
+
+ attr_accessor :pipe
+
+ ##
+ # Array of directories to search for files to satisfy an :include:
+
+ attr_accessor :rdoc_include
+
+ ##
+ # Root of the source documentation will be generated for. Set this when
+ # building documentation outside the source directory. Defaults to the
+ # current directory.
- # array of directories to search for files to satisfy an :include:
- attr_reader :rdoc_include
+ attr_accessor :root
- # title to be used out the output
- #attr_writer :title
+ ##
+ # Include the '#' at the front of hyperlinked instance method names
- # template to be used when generating output
- attr_reader :template
+ attr_accessor :show_hash
- # should diagrams be drawn
- attr_reader :diagram
+ ##
+ # Directory to copy static files from
- # should we draw fileboxes in diagrams
- attr_reader :fileboxes
+ attr_accessor :static_path
- # include the '#' at the front of hyperlinked instance method names
- attr_reader :show_hash
+ ##
+ # The number of columns in a tab
- # image format for diagrams
- attr_reader :image_format
+ attr_accessor :tab_width
- # character-set
- attr_reader :charset
+ ##
+ # Template to be used when generating output
- # should source code be included inline, or displayed in a popup
- attr_reader :inline_source
+ attr_accessor :template
- # should the output be placed into a single file
- attr_reader :all_one_file
+ ##
+ # Directory the template lives in
- # the number of columns in a tab
- attr_reader :tab_width
+ attr_accessor :template_dir
- # include line numbers in the source listings
- attr_reader :include_line_numbers
+ ##
+ # Additional template stylesheets
- # pattern for additional attr_... style methods
- attr_reader :extra_accessors
- attr_reader :extra_accessor_flags
+ attr_accessor :template_stylesheets
- # URL of stylesheet
- attr_reader :css
+ ##
+ # Documentation title
+ attr_accessor :title
+
+ ##
+ # Should RDoc update the timestamps in the output dir?
+
+ attr_accessor :update_output_dir
+
+ ##
+ # Verbosity, zero means quiet
+
+ attr_accessor :verbosity
+
+ ##
# URL of web cvs frontend
- attr_reader :webcvs
-
- # Are we promiscuous about showing module contents across
- # multiple files
- attr_reader :promiscuous
-
- module OptionList
-
- OPTION_LIST = [
- [ "--accessor", "-A", "accessorname[,..]",
- "comma separated list of additional class methods\n" +
- "that should be treated like 'attr_reader' and\n" +
- "friends. Option may be repeated. Each accessorname\n" +
- "may have '=text' appended, in which case that text\n" +
- "appears where the r/w/rw appears for normal accessors."],
-
- [ "--all", "-a", nil,
- "include all methods (not just public)\nin the output" ],
-
- [ "--charset", "-c", "charset",
- "specifies HTML character-set" ],
-
- [ "--debug", "-D", nil,
- "displays lots on internal stuff" ],
-
- [ "--diagram", "-d", nil,
- "Generate diagrams showing modules and classes.\n" +
- "You need dot V1.8.6 or later to use the --diagram\n" +
- "option correctly. Dot is available from\n"+
- "http://www.research.att.com/sw/tools/graphviz/" ],
-
- [ "--exclude", "-x", "pattern",
- "do not process files or directories matching\n" +
- "pattern. Files given explicitly on the command\n" +
- "line will never be excluded." ],
-
- [ "--extension", "-E", "new=old",
- "Treat files ending with .new as if they ended with\n" +
- ".old. Using '-E cgi=rb' will cause xxx.cgi to be\n" +
- "parsed as a Ruby file"],
-
- [ "--fileboxes", "-F", nil,
- "classes are put in boxes which represents\n" +
- "files, where these classes reside. Classes\n" +
- "shared between more than one file are\n" +
- "shown with list of files that sharing them.\n" +
- "Silently discarded if --diagram is not given\n" +
- "Experimental." ],
-
- [ "--fmt", "-f", "format name",
- "set the output formatter (see below)" ],
-
- [ "--help", "-h", nil,
- "you're looking at it" ],
-
- [ "--help-output", "-O", nil,
- "explain the various output options" ],
-
- [ "--image-format", "-I", "gif/png/jpg/jpeg",
- "Sets output image format for diagrams. Can\n" +
- "be png, gif, jpeg, jpg. If this option is\n" +
- "omitted, png is used. Requires --diagram." ],
-
- [ "--include", "-i", "dir[,dir...]",
- "set (or add to) the list of directories\n" +
- "to be searched when satisfying :include:\n" +
- "requests. Can be used more than once." ],
-
- [ "--inline-source", "-S", nil,
- "Show method source code inline, rather\n" +
- "than via a popup link" ],
-
- [ "--line-numbers", "-N", nil,
- "Include line numbers in the source code" ],
-
- [ "--main", "-m", "name",
- "'name' will be the initial page displayed" ],
-
- [ "--merge", "-M", nil,
- "when creating ri output, merge processed classes\n" +
- "into previously documented classes of the name name"],
-
- [ "--one-file", "-1", nil,
- "put all the output into a single file" ],
-
- [ "--op", "-o", "dir",
- "set the output directory" ],
-
- [ "--opname", "-n", "name",
- "Set the 'name' of the output. Has no\n" +
- "effect for HTML." ],
-
- [ "--promiscuous", "-p", nil,
- "When documenting a file that contains a module\n" +
- "or class also defined in other files, show\n" +
- "all stuff for that module/class in each files\n" +
- "page. By default, only show stuff defined in\n" +
- "that particular file." ],
-
- [ "--quiet", "-q", nil,
- "don't show progress as we parse" ],
-
- [ "--ri", "-r", nil,
- "generate output for use by 'ri.' The files are\n" +
- "stored in the '.rdoc' directory under your home\n"+
- "directory unless overridden by a subsequent\n" +
- "--op parameter, so no special privileges are needed." ],
-
- [ "--ri-site", "-R", nil,
- "generate output for use by 'ri.' The files are\n" +
- "stored in a site-wide directory, making them accessible\n"+
- "to others, so special privileges are needed." ],
-
- [ "--ri-system", "-Y", nil,
- "generate output for use by 'ri.' The files are\n" +
- "stored in a system-level directory, making them accessible\n"+
- "to others, so special privileges are needed. This option\n"+
- "is intended to be used during Ruby installations" ],
-
- [ "--show-hash", "-H", nil,
- "A name of the form #name in a comment\n" +
- "is a possible hyperlink to an instance\n" +
- "method name. When displayed, the '#' is\n" +
- "removed unless this option is specified" ],
-
- [ "--style", "-s", "stylesheet url",
- "specifies the URL of a separate stylesheet." ],
-
- [ "--tab-width", "-w", "n",
- "Set the width of tab characters (default 8)"],
-
- [ "--template", "-T", "template name",
- "Set the template used when generating output" ],
-
- [ "--title", "-t", "text",
- "Set 'txt' as the title for the output" ],
-
- [ "--version", "-v", nil,
- "display RDoc's version" ],
-
- [ "--webcvs", "-W", "url",
- "Specify a URL for linking to a web frontend\n" +
- "to CVS. If the URL contains a '\%s', the\n" +
- "name of the current file will be substituted;\n" +
- "if the URL doesn't contain a '\%s', the\n" +
- "filename will be appended to it." ],
- ]
-
- def OptionList.options
- OPTION_LIST.map do |long, short, arg,|
- [ long,
- short,
- arg ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT
- ]
+
+ attr_accessor :webcvs
+
+ ##
+ # Minimum visibility of a documented method. One of +:public+, +:protected+,
+ # +:private+ or +:nodoc+.
+ #
+ # The +:nodoc+ visibility ignores all directives related to visibility. The
+ # other visibilities may be overridden on a per-method basis with the :doc:
+ # directive.
+
+ attr_reader :visibility
+
+ def initialize # :nodoc:
+ init_ivars
+ end
+
+ def init_ivars # :nodoc:
+ @dry_run = false
+ @exclude = []
+ @files = nil
+ @force_output = false
+ @force_update = true
+ @generator = nil
+ @generator_name = nil
+ @generator_options = []
+ @generators = RDoc::RDoc::GENERATORS
+ @hyperlink_all = false
+ @line_numbers = false
+ @locale = nil
+ @locale_name = nil
+ @locale_dir = 'locale'
+ @main_page = nil
+ @markup = 'rdoc'
+ @coverage_report = false
+ @op_dir = nil
+ @page_dir = nil
+ @pipe = false
+ @output_decoration = true
+ @rdoc_include = []
+ @root = Pathname(Dir.pwd)
+ @show_hash = false
+ @static_path = []
+ @stylesheet_url = nil # TODO remove in RDoc 4
+ @tab_width = 8
+ @template = nil
+ @template_dir = nil
+ @template_stylesheets = []
+ @title = nil
+ @update_output_dir = true
+ @verbosity = 1
+ @visibility = :protected
+ @webcvs = nil
+ @write_options = false
+ @encoding = Encoding::UTF_8
+ @charset = @encoding.name
+ end
+
+ def init_with map # :nodoc:
+ init_ivars
+
+ encoding = map['encoding']
+ @encoding = encoding ? Encoding.find(encoding) : encoding
+
+ @charset = map['charset']
+ @exclude = map['exclude']
+ @generator_name = map['generator_name']
+ @hyperlink_all = map['hyperlink_all']
+ @line_numbers = map['line_numbers']
+ @locale_name = map['locale_name']
+ @locale_dir = map['locale_dir']
+ @main_page = map['main_page']
+ @markup = map['markup']
+ @op_dir = map['op_dir']
+ @show_hash = map['show_hash']
+ @tab_width = map['tab_width']
+ @template_dir = map['template_dir']
+ @title = map['title']
+ @visibility = map['visibility']
+ @webcvs = map['webcvs']
+
+ @rdoc_include = sanitize_path map['rdoc_include']
+ @static_path = sanitize_path map['static_path']
+ end
+
+ def yaml_initialize tag, map # :nodoc:
+ init_with map
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @encoding == other.encoding and
+ @generator_name == other.generator_name and
+ @hyperlink_all == other.hyperlink_all and
+ @line_numbers == other.line_numbers and
+ @locale == other.locale and
+ @locale_dir == other.locale_dir and
+ @main_page == other.main_page and
+ @markup == other.markup and
+ @op_dir == other.op_dir and
+ @rdoc_include == other.rdoc_include and
+ @show_hash == other.show_hash and
+ @static_path == other.static_path and
+ @tab_width == other.tab_width and
+ @template == other.template and
+ @title == other.title and
+ @visibility == other.visibility and
+ @webcvs == other.webcvs
+ end
+
+ ##
+ # Check that the files on the command line exist
+
+ def check_files
+ @files.delete_if do |file|
+ if File.exist? file then
+ if File.readable? file then
+ false
+ else
+ warn "file '#{file}' not readable"
+
+ true
+ end
+ else
+ warn "file '#{file}' not found"
+
+ true
end
end
+ end
+
+ ##
+ # Ensure only one generator is loaded
+
+ def check_generator
+ if @generator then
+ raise OptionParser::InvalidOption,
+ "generator already set to #{@generator_name}"
+ end
+ end
+
+ ##
+ # Set the title, but only if not already set. Used to set the title
+ # from a source file, so that a title set from the command line
+ # will have the priority.
+
+ def default_title=(string)
+ @title ||= string
+ end
+
+ ##
+ # For dumping YAML
+
+ def encode_with coder # :nodoc:
+ encoding = @encoding ? @encoding.name : nil
+
+ coder.add 'encoding', encoding
+ coder.add 'static_path', sanitize_path(@static_path)
+ coder.add 'rdoc_include', sanitize_path(@rdoc_include)
+
+ ivars = instance_variables.map { |ivar| ivar.to_s[1..-1] }
+ ivars -= SPECIAL
+
+ ivars.sort.each do |ivar|
+ coder.add ivar, instance_variable_get("@#{ivar}")
+ end
+ end
+ ##
+ # Completes any unfinished option setup business such as filtering for
+ # existent files, creating a regexp for #exclude and setting a default
+ # #template.
- def OptionList.strip_output(text)
- text =~ /^\s+/
- leading_spaces = $&
- text.gsub!(/^#{leading_spaces}/, '')
- $stdout.puts text
+ def finish
+ @op_dir ||= 'doc'
+
+ @rdoc_include << "." if @rdoc_include.empty?
+ root = @root.to_s
+ @rdoc_include << root unless @rdoc_include.include?(root)
+
+ if @exclude.nil? or Regexp === @exclude then
+ # done, #finish is being re-run
+ elsif @exclude.empty? then
+ @exclude = nil
+ else
+ @exclude = Regexp.new(@exclude.join("|"))
end
+ finish_page_dir
+
+ check_files
+
+ # If no template was specified, use the default template for the output
+ # formatter
+
+ unless @template then
+ @template = @generator_name
+ @template_dir = template_dir_for @template
+ end
+
+ if @locale_name
+ @locale = RDoc::I18n::Locale[@locale_name]
+ @locale.load(@locale_dir)
+ else
+ @locale = nil
+ end
+
+ self
+ end
+
+ ##
+ # Fixes the page_dir to be relative to the root_dir and adds the page_dir to
+ # the files list.
+
+ def finish_page_dir
+ return unless @page_dir
+
+ @files << @page_dir.to_s
+
+ page_dir = @page_dir.expand_path.relative_path_from @root
- # Show an error and exit
+ @page_dir = page_dir
+ end
+
+ ##
+ # Returns a properly-space list of generators and their descriptions.
+
+ def generator_descriptions
+ lengths = []
+
+ generators = RDoc::RDoc::GENERATORS.map do |name, generator|
+ lengths << name.length
+
+ description = generator::DESCRIPTION if
+ generator.const_defined? :DESCRIPTION
- def OptionList.error(msg)
- $stderr.puts
- $stderr.puts msg
- $stderr.puts "\nFor help on options, try 'rdoc --help'\n\n"
- exit 1
+ [name, description]
end
- # Show usage and exit
-
- def OptionList.usage(generator_names)
-
- puts
- puts(VERSION_STRING)
- puts
-
- name = File.basename($0)
- OptionList.strip_output(<<-EOT)
- Usage:
-
- #{name} [options] [names...]
-
- Files are parsed, and the information they contain
- collected, before any output is produced. This allows cross
- references between all files to be resolved. If a name is a
- directory, it is traversed. If no names are specified, all
- Ruby files in the current directory (and subdirectories) are
- processed.
-
- Options:
-
- EOT
-
- OPTION_LIST.each do |long, short, arg, desc|
- opt = sprintf("%20s", "#{long}, #{short}")
- oparg = sprintf("%-7s", arg)
- print "#{opt} #{oparg}"
- desc = desc.split("\n")
- if arg.nil? || arg.length < 7
- puts desc.shift
+ longest = lengths.max
+
+ generators.sort.map do |name, description|
+ if description then
+ " %-*s - %s" % [longest, name, description]
+ else
+ " #{name}"
+ end
+ end.join "\n"
+ end
+
+ ##
+ # Parses command line options.
+
+ def parse argv
+ ignore_invalid = true
+
+ argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
+
+ opts = OptionParser.new do |opt|
+ @option_parser = opt
+ opt.program_name = File.basename $0
+ opt.version = RDoc::VERSION
+ opt.release = nil
+ opt.summary_indent = ' ' * 4
+ opt.banner = <<-EOF
+Usage: #{opt.program_name} [options] [names...]
+
+ Files are parsed, and the information they contain collected, before any
+ output is produced. This allows cross references between all files to be
+ resolved. If a name is a directory, it is traversed. If no names are
+ specified, all Ruby files in the current directory (and subdirectories) are
+ processed.
+
+ How RDoc generates output depends on the output formatter being used, and on
+ the options you give.
+
+ Options can be specified via the RDOCOPT environment variable, which
+ functions similar to the RUBYOPT environment variable for ruby.
+
+ $ export RDOCOPT="--show-hash"
+
+ will make rdoc show hashes in method links by default. Command-line options
+ always will override those in RDOCOPT.
+
+ Available formatters:
+
+#{generator_descriptions}
+
+ RDoc understands the following file formats:
+
+ EOF
+
+ parsers = Hash.new { |h,parser| h[parser] = [] }
+
+ RDoc::Parser.parsers.each do |regexp, parser|
+ parsers[parser.name.sub('RDoc::Parser::', '')] << regexp.source
+ end
+
+ parsers.sort.each do |parser, regexp|
+ opt.banner += " - #{parser}: #{regexp.join ', '}\n"
+ end
+ opt.banner += " - TomDoc: Only in ruby files\n"
+
+ opt.banner += "\n The following options are deprecated:\n\n"
+
+ name_length = DEPRECATED.keys.sort_by { |k| k.length }.last.length
+
+ DEPRECATED.sort_by { |k,| k }.each do |name, reason|
+ opt.banner += " %*1$2$s %3$s\n" % [-name_length, name, reason]
+ end
+
+ opt.accept Template do |template|
+ template_dir = template_dir_for template
+
+ unless template_dir then
+ $stderr.puts "could not find template #{template}"
+ nil
else
- puts
+ [template, template_dir]
end
- desc.each do |line|
- puts(" "*28 + line)
+ end
+
+ opt.accept Directory do |directory|
+ directory = File.expand_path directory
+
+ raise OptionParser::InvalidArgument unless File.directory? directory
+
+ directory
+ end
+
+ opt.accept Path do |path|
+ path = File.expand_path path
+
+ raise OptionParser::InvalidArgument unless File.exist? path
+
+ path
+ end
+
+ opt.accept PathArray do |paths,|
+ paths = if paths then
+ paths.split(',').map { |d| d unless d.empty? }
+ end
+
+ paths.map do |path|
+ path = File.expand_path path
+
+ raise OptionParser::InvalidArgument unless File.exist? path
+
+ path
end
- puts
end
- puts "\nAvailable output formatters: " +
- generator_names.sort.join(', ') + "\n\n"
+ opt.separator nil
+ opt.separator "Parsing options:"
+ opt.separator nil
- puts "For information on where the output goes, use\n\n"
- puts " rdoc --help-output\n\n"
+ opt.on("--encoding=ENCODING", "-e", Encoding.list.map { |e| e.name },
+ "Specifies the output encoding. All files",
+ "read will be converted to this encoding.",
+ "The default encoding is UTF-8.",
+ "--encoding is preferred over --charset") do |value|
+ @encoding = Encoding.find value
+ @charset = @encoding.name # may not be valid value
+ end
- exit 0
- end
+ opt.separator nil
- def OptionList.help_output
- OptionList.strip_output(<<-EOT)
- How RDoc generates output depends on the output formatter being
- used, and on the options you give.
+ opt.on("--locale=NAME",
+ "Specifies the output locale.") do |value|
+ @locale_name = value
+ end
- - HTML output is normally produced into a number of separate files
- (one per class, module, and file, along with various indices).
- These files will appear in the directory given by the --op
- option (doc/ by default).
+ opt.on("--locale-data-dir=DIR",
+ "Specifies the directory where locale data live.") do |value|
+ @locale_dir = value
+ end
+
+ opt.separator nil
- - XML output by default is written to standard output. If a
- --opname option is given, the output will instead be written
- to a file with that name in the output directory.
+ opt.on("--all", "-a",
+ "Synonym for --visibility=private.") do |value|
+ @visibility = :private
+ end
- - .chm files (Windows help files) are written in the --op directory.
- If an --opname parameter is present, that name is used, otherwise
- the file will be called rdoc.chm.
+ opt.separator nil
+
+ opt.on("--exclude=PATTERN", "-x", Regexp,
+ "Do not process files or directories",
+ "matching PATTERN.") do |value|
+ @exclude << value
+ end
+
+ opt.separator nil
+
+ opt.on("--extension=NEW=OLD", "-E",
+ "Treat files ending with .new as if they",
+ "ended with .old. Using '-E cgi=rb' will",
+ "cause xxx.cgi to be parsed as a Ruby file.") do |value|
+ new, old = value.split(/=/, 2)
+
+ unless new and old then
+ raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
+ end
- For information on other RDoc options, use "rdoc --help".
- EOT
- exit 0
+ unless RDoc::Parser.alias_extension old, new then
+ raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
+ end
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]force-update", "-U",
+ "Forces rdoc to scan all sources even if",
+ "newer than the flag file.") do |value|
+ @force_update = value
+ end
+
+ opt.separator nil
+
+ opt.on("--pipe", "-p",
+ "Convert RDoc on stdin to HTML") do
+ @pipe = true
+ end
+
+ opt.separator nil
+
+ opt.on("--tab-width=WIDTH", "-w", Integer,
+ "Set the width of tab characters.") do |value|
+ raise OptionParser::InvalidArgument,
+ "#{value} is an invalid tab width" if value <= 0
+ @tab_width = value
+ end
+
+ opt.separator nil
+
+ opt.on("--visibility=VISIBILITY", "-V", RDoc::VISIBILITIES + [:nodoc],
+ "Minimum visibility to document a method.",
+ "One of 'public', 'protected' (the default),",
+ "'private' or 'nodoc' (show everything)") do |value|
+ @visibility = value
+ end
+
+ opt.separator nil
+
+ markup_formats = RDoc::Text::MARKUP_FORMAT.keys.sort
+
+ opt.on("--markup=MARKUP", markup_formats,
+ "The markup format for the named files.",
+ "The default is rdoc. Valid values are:",
+ markup_formats.join(', ')) do |value|
+ @markup = value
+ end
+
+ opt.separator nil
+
+ opt.on("--root=ROOT", Directory,
+ "Root of the source tree documentation",
+ "will be generated for. Set this when",
+ "building documentation outside the",
+ "source directory. Default is the",
+ "current directory.") do |root|
+ @root = Pathname(root)
+ end
+
+ opt.separator nil
+
+ opt.on("--page-dir=DIR", Directory,
+ "Directory where guides, your FAQ or",
+ "other pages not associated with a class",
+ "live. Set this when you don't store",
+ "such files at your project root.",
+ "NOTE: Do not use the same file name in",
+ "the page dir and the root of your project") do |page_dir|
+ @page_dir = Pathname(page_dir)
+ end
+
+ opt.separator nil
+ opt.separator "Common generator options:"
+ opt.separator nil
+
+ opt.on("--force-output", "-O",
+ "Forces rdoc to write the output files,",
+ "even if the output directory exists",
+ "and does not seem to have been created",
+ "by rdoc.") do |value|
+ @force_output = value
+ end
+
+ opt.separator nil
+
+ generator_text = @generators.keys.map { |name| " #{name}" }.sort
+
+ opt.on("-f", "--fmt=FORMAT", "--format=FORMAT", @generators.keys,
+ "Set the output formatter. One of:", *generator_text) do |value|
+ check_generator
+
+ @generator_name = value.downcase
+ setup_generator
+ end
+
+ opt.separator nil
+
+ opt.on("--include=DIRECTORIES", "-i", PathArray,
+ "Set (or add to) the list of directories to",
+ "be searched when satisfying :include:",
+ "requests. Can be used more than once.") do |value|
+ @rdoc_include.concat value.map { |dir| dir.strip }
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]coverage-report=[LEVEL]", "--[no-]dcov", "-C", Integer,
+ "Prints a report on undocumented items.",
+ "Does not generate files.") do |value|
+ value = 0 if value.nil? # Integer converts -C to nil
+
+ @coverage_report = value
+ @force_update = true if value
+ end
+
+ opt.separator nil
+
+ opt.on("--output=DIR", "--op", "-o",
+ "Set the output directory.") do |value|
+ @op_dir = value
+ end
+
+ opt.separator nil
+
+ opt.on("-d",
+ "Deprecated --diagram option.",
+ "Prevents firing debug mode",
+ "with legacy invocation.") do |value|
+ end
+
+ opt.separator nil
+ opt.separator 'HTML generator options:'
+ opt.separator nil
+
+ opt.on("--charset=CHARSET", "-c",
+ "Specifies the output HTML character-set.",
+ "Use --encoding instead of --charset if",
+ "available.") do |value|
+ @charset = value
+ end
+
+ opt.separator nil
+
+ opt.on("--hyperlink-all", "-A",
+ "Generate hyperlinks for all words that",
+ "correspond to known methods, even if they",
+ "do not start with '#' or '::' (legacy",
+ "behavior).") do |value|
+ @hyperlink_all = value
+ end
+
+ opt.separator nil
+
+ opt.on("--main=NAME", "-m",
+ "NAME will be the initial page displayed.") do |value|
+ @main_page = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]line-numbers", "-N",
+ "Include line numbers in the source code.",
+ "By default, only the number of the first",
+ "line is displayed, in a leading comment.") do |value|
+ @line_numbers = value
+ end
+
+ opt.separator nil
+
+ opt.on("--show-hash", "-H",
+ "A name of the form #name in a comment is a",
+ "possible hyperlink to an instance method",
+ "name. When displayed, the '#' is removed",
+ "unless this option is specified.") do |value|
+ @show_hash = value
+ end
+
+ opt.separator nil
+
+ opt.on("--template=NAME", "-T", Template,
+ "Set the template used when generating",
+ "output. The default depends on the",
+ "formatter used.") do |(template, template_dir)|
+ @template = template
+ @template_dir = template_dir
+ end
+
+ opt.separator nil
+
+ opt.on("--template-stylesheets=FILES", PathArray,
+ "Set (or add to) the list of files to",
+ "include with the html template.") do |value|
+ @template_stylesheets << value
+ end
+
+ opt.separator nil
+
+ opt.on("--title=TITLE", "-t",
+ "Set TITLE as the title for HTML output.") do |value|
+ @title = value
+ end
+
+ opt.separator nil
+
+ opt.on("--copy-files=PATH", Path,
+ "Specify a file or directory to copy static",
+ "files from.",
+ "If a file is given it will be copied into",
+ "the output dir. If a directory is given the",
+ "entire directory will be copied.",
+ "You can use this multiple times") do |value|
+ @static_path << value
+ end
+
+ opt.separator nil
+
+ opt.on("--webcvs=URL", "-W",
+ "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.") do |value|
+ @webcvs = value
+ end
+
+ opt.separator nil
+ opt.separator "ri generator options:"
+ opt.separator nil
+
+ opt.on("--ri", "-r",
+ "Generate output for use by `ri`. The files",
+ "are stored in the '.rdoc' directory under",
+ "your home directory unless overridden by a",
+ "subsequent --op parameter, so no special",
+ "privileges are needed.") do |value|
+ check_generator
+
+ @generator_name = "ri"
+ @op_dir ||= RDoc::RI::Paths::HOMEDIR
+ setup_generator
+ end
+
+ opt.separator nil
+
+ opt.on("--ri-site", "-R",
+ "Generate output for use by `ri`. The files",
+ "are stored in a site-wide directory,",
+ "making them accessible to others, so",
+ "special privileges are needed.") do |value|
+ check_generator
+
+ @generator_name = "ri"
+ @op_dir = RDoc::RI::Paths.site_dir
+ setup_generator
+ end
+
+ opt.separator nil
+ opt.separator "Generic options:"
+ opt.separator nil
+
+ opt.on("--write-options",
+ "Write .rdoc_options to the current",
+ "directory with the given options. Not all",
+ "options will be used. See RDoc::Options",
+ "for details.") do |value|
+ @write_options = true
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]dry-run",
+ "Don't write any files") do |value|
+ @dry_run = value
+ end
+
+ opt.separator nil
+
+ opt.on("-D", "--[no-]debug",
+ "Displays lots on internal stuff.") do |value|
+ $DEBUG_RDOC = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]ignore-invalid",
+ "Ignore invalid options and continue",
+ "(default true).") do |value|
+ ignore_invalid = value
+ end
+
+ opt.separator nil
+
+ opt.on("--quiet", "-q",
+ "Don't show progress as we parse.") do |value|
+ @verbosity = 0
+ end
+
+ opt.separator nil
+
+ opt.on("--verbose", "-V",
+ "Display extra progress as RDoc parses") do |value|
+ @verbosity = 2
+ end
+
+ opt.separator nil
+
+ opt.on("--version", "-v", "print the version") do
+ puts opt.version
+ exit
+ end
+
+ opt.separator nil
+
+ opt.on("--help", "-h", "Display this help") do
+ RDoc::RDoc::GENERATORS.each_key do |generator|
+ setup_generator generator
+ end
+
+ puts opt.help
+ exit
+ end
+
+ opt.separator nil
end
- end
- # Parse command line options. We're passed a hash containing
- # output generators, keyed by the generator name
+ setup_generator 'darkfish' if
+ argv.grep(/\A(-f|--fmt|--format|-r|-R|--ri|--ri-site)\b/).empty?
+
+ deprecated = []
+ invalid = []
- def parse(argv, generators)
- old_argv = ARGV.dup
begin
- ARGV.replace(argv)
- @op_dir = "doc"
- @op_name = nil
- @show_all = false
- @main_page = nil
- @marge = false
- @exclude = []
- @quiet = false
- @generator_name = 'html'
- @generator = generators[@generator_name]
- @rdoc_include = []
- @title = nil
- @template = nil
- @diagram = false
- @fileboxes = false
- @show_hash = false
- @image_format = 'png'
- @inline_source = false
- @all_one_file = false
- @tab_width = 8
- @include_line_numbers = false
- @extra_accessor_flags = {}
- @promiscuous = false
-
- @css = nil
- @webcvs = nil
-
- @charset = case $KCODE
- when /^S/i
- 'Shift_JIS'
- when /^E/i
- 'EUC-JP'
- else
- 'iso-8859-1'
- end
-
- accessors = []
-
- go = GetoptLong.new(*OptionList.options)
- go.quiet = true
-
- go.each do |opt, arg|
- case opt
- when "--all" then @show_all = true
- when "--charset" then @charset = arg
- when "--debug" then $DEBUG = true
- when "--exclude" then @exclude << Regexp.new(arg)
- when "--inline-source" then @inline_source = true
- when "--line-numbers" then @include_line_numbers = true
- when "--main" then @main_page = arg
- when "--merge" then @merge = true
- when "--one-file" then @all_one_file = @inline_source = true
- when "--op" then @op_dir = arg
- when "--opname" then @op_name = arg
- when "--promiscuous" then @promiscuous = true
- when "--quiet" then @quiet = true
- when "--show-hash" then @show_hash = true
- when "--style" then @css = arg
- when "--template" then @template = arg
- when "--title" then @title = arg
- when "--webcvs" then @webcvs = arg
-
- when "--accessor"
- arg.split(/,/).each do |accessor|
- if accessor =~ /^(\w+)(=(.*))?$/
- accessors << $1
- @extra_accessor_flags[$1] = $3
- end
- end
-
- when "--diagram"
- check_diagram
- @diagram = true
-
- when "--fileboxes"
- @fileboxes = true if @diagram
-
- when "--fmt"
- @generator_name = arg.downcase
- setup_generator(generators)
-
- when "--help"
- OptionList.usage(generators.keys)
-
- when "--help-output"
- OptionList.help_output
-
- when "--image-format"
- if ['gif', 'png', 'jpeg', 'jpg'].include?(arg)
- @image_format = arg
- else
- raise GetoptLong::InvalidOption.new("unknown image format: #{arg}")
- end
-
- when "--include"
- @rdoc_include.concat arg.split(/\s*,\s*/)
-
- when "--ri", "--ri-site", "--ri-system"
- @generator_name = "ri"
- @op_dir = case opt
- when "--ri" then RI::Paths::HOMEDIR
- when "--ri-site" then RI::Paths::SITEDIR
- when "--ri-system" then RI::Paths::SYSDIR
- else fail opt
- end
- setup_generator(generators)
-
- when "--tab-width"
- begin
- @tab_width = Integer(arg)
- rescue
- $stderr.puts "Invalid tab width: '#{arg}'"
- exit 1
- end
-
- when "--extension"
- new, old = arg.split(/=/, 2)
- OptionList.error("Invalid parameter to '-E'") unless new && old
- unless RDoc::ParserFactory.alias_extension(old, new)
- OptionList.error("Unknown extension .#{old} to -E")
- end
-
- when "--version"
- puts VERSION_STRING
- exit
- end
-
- end
-
- @files = ARGV.dup
-
- @rdoc_include << "." if @rdoc_include.empty?
-
- if @exclude.empty?
- @exclude = nil
+ opts.parse! argv
+ rescue OptionParser::ParseError => e
+ if DEPRECATED[e.args.first] then
+ deprecated << e.args.first
+ elsif %w[--format --ri -r --ri-site -R].include? e.args.first then
+ raise
else
- @exclude = Regexp.new(@exclude.join("|"))
+ invalid << e.args.join(' ')
end
- check_files
+ retry
+ end
- # If no template was specified, use the default
- # template for the output formatter
+ unless @generator then
+ @generator = RDoc::Generator::Darkfish
+ @generator_name = 'darkfish'
+ end
- @template ||= @generator_name
+ if @pipe and not argv.empty? then
+ @pipe = false
+ invalid << '-p (with files)'
+ end
+
+ unless quiet then
+ deprecated.each do |opt|
+ $stderr.puts 'option ' + opt + ' is deprecated: ' + DEPRECATED[opt]
+ end
+ end
+
+ unless invalid.empty? then
+ invalid = "invalid options: #{invalid.join ', '}"
- # Generate a regexp from the accessors
- unless accessors.empty?
- re = '^(' + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$'
- @extra_accessors = Regexp.new(re)
+ if ignore_invalid then
+ unless quiet then
+ $stderr.puts invalid
+ $stderr.puts '(invalid options are ignored)'
+ end
+ else
+ unless quiet then
+ $stderr.puts opts
+ end
+ $stderr.puts invalid
+ exit 1
end
+ end
+
+ @files = argv.dup
- rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error
- OptionList.error(error.message)
+ finish
- ensure
- ARGV.replace(old_argv)
+ if @write_options then
+ write_options
+ exit
end
+
+ self
end
+ ##
+ # Don't display progress as we process the files
- def title
- @title ||= "RDoc Documentation"
+ def quiet
+ @verbosity.zero?
end
-
- # Set the title, but only if not already set. This means that a title set from
- # the command line trumps one set in a source file
- def title=(string)
- @title ||= string
+ ##
+ # Set quietness to +bool+
+
+ def quiet= bool
+ @verbosity = bool ? 0 : 1
end
+ ##
+ # Removes directories from +path+ that are outside the current directory
- private
+ def sanitize_path path
+ require 'pathname'
+ dot = Pathname.new('.').expand_path
- # Set up an output generator for the format in @generator_name
- def setup_generator(generators)
- @generator = generators[@generator_name]
- if !@generator
- OptionList.error("Invalid output formatter")
- end
-
- if @generator_name == "xml"
- @all_one_file = true
- @inline_source = true
+ path.reject do |item|
+ path = Pathname.new(item).expand_path
+ relative = path.relative_path_from(dot).to_s
+ relative.start_with? '..'
end
end
- # Check that the right version of 'dot' is available.
- # Unfortuately this doesn't work correctly under Windows NT,
- # so we'll bypass the test under Windows
+ ##
+ # Set up an output generator for the named +generator_name+.
+ #
+ # If the found generator responds to :setup_options it will be called with
+ # the options instance. This allows generators to add custom options or set
+ # default options.
- def check_diagram
- return if RUBY_PLATFORM =~ /win/
+ def setup_generator generator_name = @generator_name
+ @generator = @generators[generator_name]
- ok = false
- ver = nil
- IO.popen("dot -V 2>&1") do |io|
- ver = io.read
- if ver =~ /dot.+version(?:\s+gviz)?\s+(\d+)\.(\d+)/
- ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8)
- end
+ unless @generator then
+ raise OptionParser::InvalidArgument,
+ "Invalid output formatter #{generator_name}"
end
- unless ok
- if ver =~ /^dot.+version/
- $stderr.puts "Warning: You may need dot V1.8.6 or later to use\n",
- "the --diagram option correctly. You have:\n\n ",
- ver,
- "\nDiagrams might have strange background colors.\n\n"
- else
- $stderr.puts "You need the 'dot' program to produce diagrams.",
- "(see http://www.research.att.com/sw/tools/graphviz/)\n\n"
- exit
- end
-# exit
+
+ return if @generator_options.include? @generator
+
+ @generator_name = generator_name
+ @generator_options << @generator
+
+ if @generator.respond_to? :setup_options then
+ @option_parser ||= OptionParser.new
+ @generator.setup_options self
end
end
-
- # Check that the files on the command line exist
-
- def check_files
- @files.each do |f|
- stat = File.stat f rescue error("File not found: #{f}")
- error("File '#{f}' not readable") unless stat.readable?
+
+ ##
+ # Finds the template dir for +template+
+
+ def template_dir_for template
+ template_path = File.join 'rdoc', 'generator', 'template', template
+
+ $LOAD_PATH.map do |path|
+ File.join File.expand_path(path), template_path
+ end.find do |dir|
+ File.directory? dir
+ end
+ end
+
+ # Sets the minimum visibility of a documented method.
+ #
+ # Accepts +:public+, +:protected+, +:private+, +:nodoc+, or +:all+.
+ #
+ # When +:all+ is passed, visibility is set to +:private+, similarly to
+ # RDOCOPT="--all", see #visibility for more information.
+
+ def visibility= visibility
+ case visibility
+ when :all
+ @visibility = :private
+ else
+ @visibility = visibility
end
end
- def error(str)
- $stderr.puts str
- exit(1)
+ ##
+ # Displays a warning using Kernel#warn if we're being verbose
+
+ def warn message
+ super message if @verbosity > 1
+ end
+
+ ##
+ # Writes the YAML file .rdoc_options to the current directory containing the
+ # parsed options.
+
+ def write_options
+ RDoc.load_yaml
+
+ open '.rdoc_options', 'w' do |io|
+ io.set_encoding Encoding::UTF_8
+
+ YAML.dump self, io
+ end
end
end
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
new file mode 100644
index 0000000000..2b826d9284
--- /dev/null
+++ b/lib/rdoc/parser.rb
@@ -0,0 +1,277 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: true
+
+##
+# A parser is simple a class that subclasses RDoc::Parser and implements #scan
+# to fill in an RDoc::TopLevel with parsed data.
+#
+# The initialize method takes an RDoc::TopLevel to fill with parsed content,
+# the name of the file to be parsed, the content of the file, an RDoc::Options
+# object and an RDoc::Stats object to inform the user of parsed items. The
+# scan method is then called to parse the file and must return the
+# RDoc::TopLevel object. By calling super these items will be set for you.
+#
+# In order to be used by RDoc the parser needs to register the file extensions
+# it can parse. Use ::parse_files_matching to register extensions.
+#
+# require 'rdoc'
+#
+# class RDoc::Parser::Xyz < RDoc::Parser
+# parse_files_matching /\.xyz$/
+#
+# def initialize top_level, file_name, content, options, stats
+# super
+#
+# # extra initialization if needed
+# end
+#
+# def scan
+# # parse file and fill in @top_level
+# end
+# end
+
+class RDoc::Parser
+
+ @parsers = []
+
+ class << self
+
+ ##
+ # An Array of arrays that maps file extension (or name) regular
+ # expressions to parser classes that will parse matching filenames.
+ #
+ # Use parse_files_matching to register a parser's file extensions.
+
+ attr_reader :parsers
+
+ end
+
+ ##
+ # The name of the file being parsed
+
+ attr_reader :file_name
+
+ ##
+ # Alias an extension to another extension. After this call, files ending
+ # "new_ext" will be parsed using the same parser as "old_ext"
+
+ def self.alias_extension(old_ext, new_ext)
+ old_ext = old_ext.sub(/^\.(.*)/, '\1')
+ new_ext = new_ext.sub(/^\.(.*)/, '\1')
+
+ parser = can_parse_by_name "xxx.#{old_ext}"
+ return false unless parser
+
+ RDoc::Parser.parsers.unshift [/\.#{new_ext}$/, parser]
+
+ true
+ end
+
+ ##
+ # Determines if the file is a "binary" file which basically means it has
+ # content that an RDoc parser shouldn't try to consume.
+
+ def self.binary?(file)
+ return false if file =~ /\.(rdoc|txt)$/
+
+ s = File.read(file, 1024) or return false
+
+ return true if s[0, 2] == Marshal.dump('')[0, 2] or s.index("\x00")
+
+ mode = 'r:utf-8' # default source encoding has been chagened to utf-8
+ s.sub!(/\A#!.*\n/, '') # assume shebang line isn't longer than 1024.
+ encoding = s[/^\s*\#\s*(?:-\*-\s*)?(?:en)?coding:\s*([^\s;]+?)(?:-\*-|[\s;])/, 1]
+ mode = "rb:#{encoding}" if encoding
+ s = File.open(file, mode) {|f| f.gets(nil, 1024)}
+
+ not s.valid_encoding?
+ end
+
+ ##
+ # Checks if +file+ is a zip file in disguise. Signatures from
+ # http://www.garykessler.net/library/file_sigs.html
+
+ def self.zip? file
+ zip_signature = File.read file, 4
+
+ zip_signature == "PK\x03\x04" or
+ zip_signature == "PK\x05\x06" or
+ zip_signature == "PK\x07\x08"
+ rescue
+ false
+ end
+
+ ##
+ # Return a parser that can handle a particular extension
+
+ def self.can_parse file_name
+ parser = can_parse_by_name file_name
+
+ # HACK Selenium hides a jar file using a .txt extension
+ return if parser == RDoc::Parser::Simple and zip? file_name
+
+ parser
+ end
+
+ ##
+ # Returns a parser that can handle the extension for +file_name+. This does
+ # not depend upon the file being readable.
+
+ def self.can_parse_by_name file_name
+ _, parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }
+
+ # The default parser must not parse binary files
+ ext_name = File.extname file_name
+ return parser if ext_name.empty?
+
+ if parser == RDoc::Parser::Simple and ext_name !~ /txt|rdoc/ then
+ case check_modeline file_name
+ when nil, 'rdoc' then # continue
+ else return nil
+ end
+ end
+
+ parser
+ rescue Errno::EACCES
+ end
+
+ ##
+ # Returns the file type from the modeline in +file_name+
+
+ def self.check_modeline file_name
+ line = open file_name do |io|
+ io.gets
+ end
+
+ /-\*-\s*(.*?\S)\s*-\*-/ =~ line
+
+ return nil unless type = $1
+
+ if /;/ =~ type then
+ return nil unless /(?:\s|\A)mode:\s*([^\s;]+)/i =~ type
+ type = $1
+ end
+
+ return nil if /coding:/i =~ type
+
+ type.downcase
+ rescue ArgumentError
+ rescue Encoding::InvalidByteSequenceError # invalid byte sequence
+
+ end
+
+ ##
+ # Finds and instantiates the correct parser for the given +file_name+ and
+ # +content+.
+
+ def self.for top_level, file_name, content, options, stats
+ return if binary? file_name
+
+ parser = use_markup content
+
+ unless parser then
+ parse_name = file_name
+
+ # If no extension, look for shebang
+ if file_name !~ /\.\w+$/ && content =~ %r{\A#!(.+)} then
+ shebang = $1
+ case shebang
+ when %r{env\s+ruby}, %r{/ruby}
+ parse_name = 'dummy.rb'
+ end
+ end
+
+ parser = can_parse parse_name
+ end
+
+ return unless parser
+
+ content = remove_modeline content
+
+ parser.new top_level, file_name, content, options, stats
+ rescue SystemCallError
+ nil
+ end
+
+ ##
+ # Record which file types this parser can understand.
+ #
+ # It is ok to call this multiple times.
+
+ def self.parse_files_matching(regexp)
+ RDoc::Parser.parsers.unshift [regexp, self]
+ end
+
+ ##
+ # Removes an emacs-style modeline from the first line of the document
+
+ def self.remove_modeline content
+ content.sub(/\A.*-\*-\s*(.*?\S)\s*-\*-.*\r?\n/, '')
+ end
+
+ ##
+ # If there is a <tt>markup: parser_name</tt> comment at the front of the
+ # file, use it to determine the parser. For example:
+ #
+ # # markup: rdoc
+ # # Class comment can go here
+ #
+ # class C
+ # end
+ #
+ # The comment should appear as the first line of the +content+.
+ #
+ # If the content contains a shebang or editor modeline the comment may
+ # appear on the second or third line.
+ #
+ # Any comment style may be used to hide the markup comment.
+
+ def self.use_markup content
+ markup = content.lines.first(3).grep(/markup:\s+(\w+)/) { $1 }.first
+
+ return unless markup
+
+ # TODO Ruby should be returned only when the filename is correct
+ return RDoc::Parser::Ruby if %w[tomdoc markdown].include? markup
+
+ markup = Regexp.escape markup
+
+ _, selected = RDoc::Parser.parsers.find do |_, parser|
+ /^#{markup}$/i =~ parser.name.sub(/.*:/, '')
+ end
+
+ selected
+ end
+
+ ##
+ # Creates a new Parser storing +top_level+, +file_name+, +content+,
+ # +options+ and +stats+ in instance variables. In +@preprocess+ an
+ # RDoc::Markup::PreProcess object is created which allows processing of
+ # directives.
+
+ def initialize top_level, file_name, content, options, stats
+ @top_level = top_level
+ @top_level.parser = self.class
+ @store = @top_level.store
+
+ @file_name = file_name
+ @content = content
+ @options = options
+ @stats = stats
+
+ @preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+ @preprocess.options = @options
+ end
+
+ autoload :RubyTools, 'rdoc/parser/ruby_tools'
+ autoload :Text, 'rdoc/parser/text'
+
+end
+
+# simple must come first in order to show up last in the parsers list
+require 'rdoc/parser/simple'
+require 'rdoc/parser/c'
+require 'rdoc/parser/changelog'
+require 'rdoc/parser/markdown'
+require 'rdoc/parser/rd'
+require 'rdoc/parser/ruby'
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
new file mode 100644
index 0000000000..183538d54b
--- /dev/null
+++ b/lib/rdoc/parser/c.rb
@@ -0,0 +1,1268 @@
+# frozen_string_literal: true
+require 'tsort'
+
+##
+# RDoc::Parser::C attempts to parse C extension files. It looks for
+# the standard patterns that you find in extensions: <tt>rb_define_class,
+# rb_define_method</tt> and so on. It tries to find the corresponding
+# C source for the methods and extract comments, but if we fail
+# we don't worry too much.
+#
+# The comments associated with a Ruby method are extracted from the C
+# comment block associated with the routine that _implements_ that
+# method, that is to say the method whose name is given in the
+# <tt>rb_define_method</tt> call. For example, you might write:
+#
+# /*
+# * Returns a new array that is a one-dimensional flattening of this
+# * array (recursively). That is, for every element that is an array,
+# * extract its elements into the new array.
+# *
+# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# */
+# static VALUE
+# rb_ary_flatten(ary)
+# VALUE ary;
+# {
+# ary = rb_obj_dup(ary);
+# rb_ary_flatten_bang(ary);
+# return ary;
+# }
+#
+# ...
+#
+# void
+# Init_Array()
+# {
+# ...
+# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+#
+# Here RDoc will determine from the rb_define_method line that there's a
+# method called "flatten" in class Array, and will look for the implementation
+# in the method rb_ary_flatten. It will then use the comment from that
+# method in the HTML output. This method must be in the same source file
+# as the rb_define_method.
+#
+# The comment blocks may include special directives:
+#
+# [Document-class: +name+]
+# Documentation for the named class.
+#
+# [Document-module: +name+]
+# Documentation for the named module.
+#
+# [Document-const: +name+]
+# Documentation for the named +rb_define_const+.
+#
+# Constant values can be supplied on the first line of the comment like so:
+#
+# /* 300: The highest possible score in bowling */
+# rb_define_const(cFoo, "PERFECT", INT2FIX(300));
+#
+# The value can contain internal colons so long as they are escaped with a \
+#
+# [Document-global: +name+]
+# Documentation for the named +rb_define_global_const+
+#
+# [Document-variable: +name+]
+# Documentation for the named +rb_define_variable+
+#
+# [Document-method: +method_name+]
+# Documentation for the named method. Use this when the method name is
+# unambiguous.
+#
+# [Document-method: <tt>ClassName::method_name<tt>]
+# Documentation for a singleton method in the given class. Use this when
+# the method name alone is ambiguous.
+#
+# [Document-method: <tt>ClassName#method_name<tt>]
+# Documentation for a instance method in the given class. Use this when the
+# method name alone is ambiguous.
+#
+# [Document-attr: +name+]
+# Documentation for the named attribute.
+#
+# [call-seq: <i>text up to an empty line</i>]
+# Because C source doesn't give descriptive names to Ruby-level parameters,
+# you need to document the calling sequence explicitly
+#
+# In addition, RDoc assumes by default that the C method implementing a
+# Ruby function is in the same source file as the rb_define_method call.
+# If this isn't the case, add the comment:
+#
+# rb_define_method(....); // in filename
+#
+# As an example, we might have an extension that defines multiple classes
+# in its Init_xxx method. We could document them using
+#
+# /*
+# * Document-class: MyClass
+# *
+# * Encapsulate the writing and reading of the configuration
+# * file. ...
+# */
+#
+# /*
+# * Document-method: read_value
+# *
+# * call-seq:
+# * cfg.read_value(key) -> value
+# * cfg.read_value(key} { |key| } -> value
+# *
+# * Return the value corresponding to +key+ from the configuration.
+# * In the second form, if the key isn't found, invoke the
+# * block and return its value.
+# */
+
+class RDoc::Parser::C < RDoc::Parser
+
+ parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
+
+ include RDoc::Text
+
+ ##
+ # Maps C variable names to names of Ruby classes or modules
+
+ attr_reader :classes
+
+ ##
+ # C file the parser is parsing
+
+ attr_accessor :content
+
+ ##
+ # Dependencies from a missing enclosing class to the classes in
+ # missing_dependencies that depend upon it.
+
+ attr_reader :enclosure_dependencies
+
+ ##
+ # Maps C variable names to names of Ruby classes (and singleton classes)
+
+ attr_reader :known_classes
+
+ ##
+ # Classes found while parsing the C file that were not yet registered due to
+ # a missing enclosing class. These are processed by do_missing
+
+ attr_reader :missing_dependencies
+
+ ##
+ # Maps C variable names to names of Ruby singleton classes
+
+ attr_reader :singleton_classes
+
+ ##
+ # The TopLevel items in the parsed file belong to
+
+ attr_reader :top_level
+
+ ##
+ # Prepares for parsing a C file. See RDoc::Parser#initialize for details on
+ # the arguments.
+
+ def initialize top_level, file_name, content, options, stats
+ super
+
+ @known_classes = RDoc::KNOWN_CLASSES.dup
+ @content = handle_tab_width handle_ifdefs_in @content
+ @file_dir = File.dirname @file_name
+
+ @classes = load_variable_map :c_class_variables
+ @singleton_classes = load_variable_map :c_singleton_class_variables
+
+ # class_variable => { function => [method, ...] }
+ @methods = Hash.new { |h, f| h[f] = Hash.new { |i, m| i[m] = [] } }
+
+ # missing variable => [handle_class_module arguments]
+ @missing_dependencies = {}
+
+ # missing enclosure variable => [dependent handle_class_module arguments]
+ @enclosure_dependencies = Hash.new { |h, k| h[k] = [] }
+ @enclosure_dependencies.instance_variable_set :@missing_dependencies,
+ @missing_dependencies
+
+ @enclosure_dependencies.extend TSort
+
+ def @enclosure_dependencies.tsort_each_node &block
+ each_key(&block)
+ rescue TSort::Cyclic => e
+ cycle_vars = e.message.scan(/"(.*?)"/).flatten
+
+ cycle = cycle_vars.sort.map do |var_name|
+ delete var_name
+
+ var_name, type, mod_name, = @missing_dependencies[var_name]
+
+ "#{type} #{mod_name} (#{var_name})"
+ end.join ', '
+
+ warn "Unable to create #{cycle} due to a cyclic class or module creation"
+
+ retry
+ end
+
+ def @enclosure_dependencies.tsort_each_child node, &block
+ fetch(node, []).each(&block)
+ end
+ end
+
+ ##
+ # Removes duplicate call-seq entries for methods using the same
+ # implementation.
+
+ def deduplicate_call_seq
+ @methods.each do |var_name, functions|
+ class_name = @known_classes[var_name]
+ class_obj = find_class var_name, class_name
+
+ functions.each_value do |method_names|
+ next if method_names.length == 1
+
+ method_names.each do |method_name|
+ deduplicate_method_name class_obj, method_name
+ end
+ end
+ end
+ end
+
+ ##
+ # If two ruby methods share a C implementation (and comment) this
+ # deduplicates the examples in the call_seq for the method to reduce
+ # confusion in the output.
+
+ def deduplicate_method_name class_obj, method_name # :nodoc:
+ return unless
+ method = class_obj.method_list.find { |m| m.name == method_name }
+ return unless call_seq = method.call_seq
+
+ method_name = method_name[0, 1] if method_name =~ /\A\[/
+
+ entries = call_seq.split "\n"
+
+ matching = entries.select do |entry|
+ entry =~ /^\w*\.?#{Regexp.escape method_name}/ or
+ entry =~ /\s#{Regexp.escape method_name}\s/
+ end
+
+ method.call_seq = matching.join "\n"
+ end
+
+ ##
+ # Scans #content for rb_define_alias
+
+ def do_aliases
+ @content.scan(/rb_define_alias\s*\(
+ \s*(\w+),
+ \s*"(.+?)",
+ \s*"(.+?)"
+ \s*\)/xm) do |var_name, new_name, old_name|
+ class_name = @known_classes[var_name]
+
+ unless class_name then
+ @options.warn "Enclosing class or module %p for alias %s %s is not known" % [
+ var_name, new_name, old_name]
+ next
+ end
+
+ class_obj = find_class var_name, class_name
+
+ al = RDoc::Alias.new '', old_name, new_name, ''
+ al.singleton = @singleton_classes.key? var_name
+
+ comment = find_alias_comment var_name, new_name, old_name
+
+ comment.normalize
+
+ al.comment = comment
+
+ al.record_location @top_level
+
+ class_obj.add_alias al
+ @stats.add_alias al
+ end
+ end
+
+ ##
+ # Scans #content for rb_attr and rb_define_attr
+
+ def do_attrs
+ @content.scan(/rb_attr\s*\(
+ \s*(\w+),
+ \s*([\w"()]+),
+ \s*([01]),
+ \s*([01]),
+ \s*\w+\);/xm) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
+ end
+
+ @content.scan(%r%rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(\d+),
+ \s*(\d+)\s*\);
+ %xm) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
+ end
+ end
+
+ ##
+ # Scans #content for boot_defclass
+
+ def do_boot_defclass
+ @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
+ |var_name, class_name, parent|
+ parent = nil if parent == "0"
+ handle_class_module(var_name, :class, class_name, parent, nil)
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_class, boot_defclass, rb_define_class_under
+ # and rb_singleton_class
+
+ def do_classes
+ do_boot_defclass
+ do_define_class
+ do_define_class_under
+ do_singleton_class
+ do_struct_define_without_accessor
+ end
+
+ ##
+ # Scans #content for rb_define_variable, rb_define_readonly_variable,
+ # rb_define_const and rb_define_global_const
+
+ def do_constants
+ @content.scan(%r%\Wrb_define_
+ ( variable |
+ readonly_variable |
+ const |
+ global_const )
+ \s*\(
+ (?:\s*(\w+),)?
+ \s*"(\w+)",
+ \s*(.*?)\s*\)\s*;
+ %xm) do |type, var_name, const_name, definition|
+ var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
+ handle_constants type, var_name, const_name, definition
+ end
+
+ @content.scan(%r%
+ \Wrb_curses_define_const
+ \s*\(
+ \s*
+ (\w+)
+ \s*
+ \)
+ \s*;%xm) do |consts|
+ const = consts.first
+
+ handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})"
+ end
+
+ @content.scan(%r%
+ \Wrb_file_const
+ \s*\(
+ \s*
+ "([^"]+)",
+ \s*
+ (.*?)
+ \s*
+ \)
+ \s*;%xm) do |name, value|
+ handle_constants 'const', 'rb_mFConst', name, value
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_class
+
+ def do_define_class
+ # The '.' lets us handle SWIG-generated files
+ @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
+ \(
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \)/mx) do |var_name, class_name, parent|
+ handle_class_module(var_name, :class, class_name, parent, nil)
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_class_under
+
+ def do_define_class_under
+ @content.scan(/([\w\.]+)\s* = # var_name
+ \s*rb_define_class_under\s*
+ \(
+ \s* (\w+), # under
+ \s* "(\w+)", # class_name
+ \s*
+ (?:
+ ([\w\*\s\(\)\.\->]+) | # parent_name
+ rb_path2class\("([\w:]+)"\) # path
+ )
+ \s*
+ \)
+ /mx) do |var_name, under, class_name, parent_name, path|
+ parent = path || parent_name
+
+ handle_class_module var_name, :class, class_name, parent, under
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_module
+
+ def do_define_module
+ @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
+ |var_name, class_name|
+ handle_class_module(var_name, :module, class_name, nil, nil)
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_module_under
+
+ def do_define_module_under
+ @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)"
+ \s*\)/mx) do |var_name, in_module, class_name|
+ handle_class_module(var_name, :module, class_name, nil, in_module)
+ end
+ end
+
+ ##
+ # Scans #content for rb_include_module
+
+ def do_includes
+ @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
+ next unless cls = @classes[c]
+ m = @known_classes[m] || m
+
+ comment = RDoc::Comment.new '', @top_level
+ incl = cls.add_include RDoc::Include.new(m, comment)
+ incl.record_location @top_level
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_method, rb_define_singleton_method,
+ # rb_define_module_function, rb_define_private_method,
+ # rb_define_global_function and define_filetest_function
+
+ def do_methods
+ @content.scan(%r%rb_define_
+ (
+ singleton_method |
+ method |
+ module_function |
+ private_method
+ )
+ \s*\(\s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\(|\(METHOD\))?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))?
+ %xm) do |type, var_name, meth_name, function, param_count, source_file|
+
+ # Ignore top-object and weird struct.c dynamic stuff
+ next if var_name == "ruby_top_self"
+ next if var_name == "nstr"
+
+ var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_method(type, var_name, meth_name, function, param_count,
+ source_file)
+ end
+
+ @content.scan(%r%rb_define_global_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ %xm) do |meth_name, function, param_count, source_file|
+ handle_method("method", "rb_mKernel", meth_name, function, param_count,
+ source_file)
+ end
+
+ @content.scan(/define_filetest_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count|
+
+ handle_method("method", "rb_mFileTest", meth_name, function, param_count)
+ handle_method("singleton_method", "rb_cFile", meth_name, function,
+ param_count)
+ end
+ end
+
+ ##
+ # Creates classes and module that were missing were defined due to the file
+ # order being different than the declaration order.
+
+ def do_missing
+ return if @missing_dependencies.empty?
+
+ @enclosure_dependencies.tsort.each do |in_module|
+ arguments = @missing_dependencies.delete in_module
+
+ next unless arguments # dependency on existing class
+
+ handle_class_module(*arguments)
+ end
+ end
+
+ ##
+ # Scans #content for rb_define_module and rb_define_module_under
+
+ def do_modules
+ do_define_module
+ do_define_module_under
+ end
+
+ ##
+ # Scans #content for rb_singleton_class
+
+ def do_singleton_class
+ @content.scan(/([\w\.]+)\s* = \s*rb_singleton_class\s*
+ \(
+ \s*(\w+)
+ \s*\)/mx) do |sclass_var, class_var|
+ handle_singleton sclass_var, class_var
+ end
+ end
+
+ ##
+ # Scans #content for struct_define_without_accessor
+
+ def do_struct_define_without_accessor
+ @content.scan(/([\w\.]+)\s* = \s*rb_struct_define_without_accessor\s*
+ \(
+ \s*"(\w+)", # Class name
+ \s*(\w+), # Parent class
+ \s*\w+, # Allocation function
+ (\s*"\w+",)* # Attributes
+ \s*NULL
+ \)/mx) do |var_name, class_name, parent|
+ handle_class_module(var_name, :class, class_name, parent, nil)
+ end
+ end
+
+ ##
+ # Finds the comment for an alias on +class_name+ from +new_name+ to
+ # +old_name+
+
+ def find_alias_comment class_name, new_name, old_name
+ content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
+ \s*"#{Regexp.escape new_name}"\s*,
+ \s*"#{Regexp.escape old_name}"\s*\);%xm
+
+ RDoc::Comment.new($1 || '', @top_level)
+ end
+
+ ##
+ # Finds a comment for rb_define_attr, rb_attr or Document-attr.
+ #
+ # +var_name+ is the C class variable the attribute is defined on.
+ # +attr_name+ is the attribute's name.
+ #
+ # +read+ and +write+ are the read/write flags ('1' or '0'). Either both or
+ # neither must be provided.
+
+ def find_attr_comment var_name, attr_name, read = nil, write = nil
+ attr_name = Regexp.escape attr_name
+
+ rw = if read and write then
+ /\s*#{read}\s*,\s*#{write}\s*/xm
+ else
+ /.*?/m
+ end
+
+ comment = if @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*#{var_name},)?\s*
+ "#{attr_name}"\s*,
+ #{rw}\)\s*;%xm then
+ $1
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_attr\(\s*#{var_name}\s*,
+ \s*#{attr_name}\s*,
+ #{rw},.*?\)\s*;%xm then
+ $1
+ elsif @content =~ %r%(/\*.*?(?:\s*\*\s*)?)
+ Document-attr:\s#{attr_name}\s*?\n
+ ((?>(.|\n)*?\*/))%x then
+ "#{$1}\n#{$2}"
+ else
+ ''
+ end
+
+ RDoc::Comment.new comment, @top_level
+ end
+
+ ##
+ # Generate a Ruby-method table
+
+ def gen_body_table file_content
+ table = {}
+ file_content.scan(%r{
+ ((?>/\*.*?\*/\s*)?)
+ ((?:(?:\w+)\s+)?
+ (?:intern\s+)?VALUE\s+(\w+)
+ \s*(?:\([^)]*\))(?:[^;]|$))
+ | ((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+(\w+)\s+(\w+))
+ | ^\s*\#\s*define\s+(\w+)\s+(\w+)
+ }xm) do
+ case
+ when $1
+ table[$3] = [:func_def, $1, $2, $~.offset(2)] if !table[$3] || table[$3][0] != :func_def
+ when $4
+ table[$6] = [:macro_def, $4, $5, $~.offset(5), $7] if !table[$6] || table[$6][0] == :macro_alias
+ when $8
+ table[$8] ||= [:macro_alias, $9]
+ end
+ end
+ table
+ end
+
+ ##
+ # Find the C code corresponding to a Ruby method
+
+ def find_body class_name, meth_name, meth_obj, file_content, quiet = false
+ if file_content
+ @body_table ||= {}
+ @body_table[file_content] ||= gen_body_table file_content
+ type, *args = @body_table[file_content][meth_name]
+ end
+
+ case type
+ when :func_def
+ comment = RDoc::Comment.new args[0], @top_level
+ body = args[1]
+ offset, = args[2]
+
+ comment.remove_private if comment
+
+ # try to find the whole body
+ body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content
+
+ # The comment block may have been overridden with a 'Document-method'
+ # block. This happens in the interpreter when multiple methods are
+ # vectored through to the same C method but those methods are logically
+ # distinct (for example Kernel.hash and Kernel.object_id share the same
+ # implementation
+
+ override_comment = find_override_comment class_name, meth_obj
+ comment = override_comment if override_comment
+
+ comment.normalize
+ find_modifiers comment, meth_obj if comment
+
+ #meth_obj.params = params
+ meth_obj.start_collecting_tokens
+ tk = { :line_no => 1, :char_no => 1, :text => body }
+ meth_obj.add_token tk
+ meth_obj.comment = comment
+ meth_obj.line = file_content[0, offset].count("\n") + 1
+
+ body
+ when :macro_def
+ comment = RDoc::Comment.new args[0], @top_level
+ body = args[1]
+ offset, = args[2]
+
+ find_body class_name, args[3], meth_obj, file_content, true
+
+ comment.normalize
+ find_modifiers comment, meth_obj
+
+ meth_obj.start_collecting_tokens
+ tk = { :line_no => 1, :char_no => 1, :text => body }
+ meth_obj.add_token tk
+ meth_obj.comment = comment
+ meth_obj.line = file_content[0, offset].count("\n") + 1
+
+ body
+ when :macro_alias
+ # with no comment we hope the aliased definition has it and use it's
+ # definition
+
+ body = find_body(class_name, args[0], meth_obj, file_content, true)
+
+ return body if body
+
+ @options.warn "No definition for #{meth_name}"
+ false
+ else # No body, but might still have an override comment
+ comment = find_override_comment class_name, meth_obj
+
+ if comment then
+ comment.normalize
+ find_modifiers comment, meth_obj
+ meth_obj.comment = comment
+
+ ''
+ else
+ @options.warn "No definition for #{meth_name}"
+ false
+ end
+ end
+ end
+
+ ##
+ # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+
+
+ def find_class(raw_name, name)
+ unless @classes[raw_name]
+ if raw_name =~ /^rb_m/
+ container = @top_level.add_module RDoc::NormalModule, name
+ else
+ container = @top_level.add_class RDoc::NormalClass, name
+ end
+
+ container.record_location @top_level
+ @classes[raw_name] = container
+ end
+ @classes[raw_name]
+ end
+
+ ##
+ # Look for class or module documentation above Init_+class_name+(void),
+ # in a Document-class +class_name+ (or module) comment or above an
+ # rb_define_class (or module). If a comment is supplied above a matching
+ # Init_ and a rb_define_class the Init_ comment is used.
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # Init_Foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * Document-class: Foo
+ # * This is a comment for Foo
+ # */
+ # Init_foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+
+ def find_class_comment class_name, class_mod
+ comment = nil
+
+ if @content =~ %r%
+ ((?>/\*.*?\*/\s+))
+ (static\s+)?
+ void\s+
+ Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xmi then
+ comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
+ elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
+ (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm then
+ comment = "/*\n#{$1}"
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ ([\w\.\s]+\s* = \s+)?rb_define_(class|module)[\t (]*?"(#{class_name})"%xm then
+ comment = $1
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ ([\w\. \t]+ = \s+)?rb_define_(class|module)_under[\t\w, (]*?"(#{class_name.split('::').last})"%xm then
+ comment = $1
+ else
+ comment = ''
+ end
+
+ comment = RDoc::Comment.new comment, @top_level
+ comment.normalize
+
+ look_for_directives_in class_mod, comment
+
+ class_mod.add_comment comment, @top_level
+ end
+
+ ##
+ # Generate a const table
+
+ def gen_const_table file_content
+ table = {}
+ @content.scan(%r{
+ ((?>^\s*/\*.*?\*/\s+))
+ rb_define_(\w+)\((?:\s*(?:\w+),)?\s*
+ "(\w+)"\s*,
+ .*?\)\s*;
+ | Document-(?:const|global|variable):\s
+ ((?:\w+::)*\w+)
+ \s*?\n((?>.*?\*/))
+ }mxi) do
+ case
+ when $1 then table[[$2, $3]] = $1
+ when $4 then table[$4] = "/*\n" + $5
+ end
+ end
+ table
+ end
+
+ ##
+ # Finds a comment matching +type+ and +const_name+ either above the
+ # comment or in the matching Document- section.
+
+ def find_const_comment(type, const_name, class_name = nil)
+ @const_table ||= {}
+ @const_table[@content] ||= gen_const_table @content
+ table = @const_table[@content]
+
+ comment =
+ table[[type, const_name]] ||
+ (class_name && table[class_name + "::" + const_name]) ||
+ table[const_name] ||
+ ''
+
+ RDoc::Comment.new comment, @top_level
+ end
+
+ ##
+ # Handles modifiers in +comment+ and updates +meth_obj+ as appropriate.
+
+ def find_modifiers comment, meth_obj
+ comment.normalize
+ comment.extract_call_seq meth_obj
+
+ look_for_directives_in meth_obj, comment
+ end
+
+ ##
+ # Finds a <tt>Document-method</tt> override for +meth_obj+ on +class_name+
+
+ def find_override_comment class_name, meth_obj
+ name = Regexp.escape meth_obj.name
+ prefix = Regexp.escape meth_obj.name_prefix
+
+ comment = if @content =~ %r%Document-method:
+ \s+#{class_name}#{prefix}#{name}
+ \s*?\n((?>.*?\*/))%xm then
+ "/*#{$1}"
+ elsif @content =~ %r%Document-method:
+ \s#{name}\s*?\n((?>.*?\*/))%xm then
+ "/*#{$1}"
+ end
+
+ return unless comment
+
+ RDoc::Comment.new comment, @top_level
+ end
+
+ ##
+ # Creates a new RDoc::Attr +attr_name+ on class +var_name+ that is either
+ # +read+, +write+ or both
+
+ def handle_attr(var_name, attr_name, read, write)
+ rw = ''
+ rw += 'R' if '1' == read
+ rw += 'W' if '1' == write
+
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class var_name, class_name
+
+ return unless class_obj
+
+ comment = find_attr_comment var_name, attr_name
+ comment.normalize
+
+ name = attr_name.gsub(/rb_intern(?:_const)?\("([^"]+)"\)/, '\1')
+
+ attr = RDoc::Attr.new '', name, rw, comment
+
+ attr.record_location @top_level
+ class_obj.add_attribute attr
+ @stats.add_attribute attr
+ end
+
+ ##
+ # Creates a new RDoc::NormalClass or RDoc::NormalModule based on +type+
+ # named +class_name+ in +parent+ which was assigned to the C +var_name+.
+
+ def handle_class_module(var_name, type, class_name, parent, in_module)
+ parent_name = @known_classes[parent] || parent
+
+ if in_module then
+ enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
+
+ if enclosure.nil? and enclosure = @known_classes[in_module] then
+ enc_type = /^rb_m/ =~ in_module ? :module : :class
+ handle_class_module in_module, enc_type, enclosure, nil, nil
+ enclosure = @classes[in_module]
+ end
+
+ unless enclosure then
+ @enclosure_dependencies[in_module] << var_name
+ @missing_dependencies[var_name] =
+ [var_name, type, class_name, parent, in_module]
+
+ return
+ end
+ else
+ enclosure = @top_level
+ end
+
+ if type == :class then
+ full_name = if RDoc::ClassModule === enclosure then
+ enclosure.full_name + "::#{class_name}"
+ else
+ class_name
+ end
+
+ if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)% then
+ parent_name = $1
+ end
+
+ cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
+ else
+ cm = enclosure.add_module RDoc::NormalModule, class_name
+ end
+
+ cm.record_location enclosure.top_level
+
+ find_class_comment cm.full_name, cm
+
+ case cm
+ when RDoc::NormalClass
+ @stats.add_class cm
+ when RDoc::NormalModule
+ @stats.add_module cm
+ end
+
+ @classes[var_name] = cm
+ @known_classes[var_name] = cm.full_name
+ @store.add_c_enclosure var_name, cm
+ end
+
+ ##
+ # Adds constants. By providing some_value: at the start of the comment you
+ # can override the C value of the comment to give a friendly definition.
+ #
+ # /* 300: The perfect score in bowling */
+ # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
+ #
+ # Will override <tt>INT2FIX(300)</tt> with the value +300+ in the output
+ # RDoc. Values may include quotes and escaped colons (\:).
+
+ def handle_constants(type, var_name, const_name, definition)
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class var_name, class_name
+
+ unless class_obj then
+ @options.warn 'Enclosing class or module %p is not known' % [const_name]
+ return
+ end
+
+ comment = find_const_comment type, const_name, class_name
+ comment.normalize
+
+ # In the case of rb_define_const, the definition and comment are in
+ # "/* definition: comment */" form. The literal ':' and '\' characters
+ # can be escaped with a backslash.
+ if type.downcase == 'const' then
+ no_match, new_definition, new_comment = comment.text.split(/(\A.*):/)
+
+ if no_match and no_match.empty? then
+ if new_definition.empty? then # Default to literal C definition
+ new_definition = definition
+ else
+ new_definition = new_definition.gsub("\:", ":")
+ new_definition = new_definition.gsub("\\", '\\')
+ end
+
+ new_definition.sub!(/\A(\s+)/, '')
+
+ new_comment = "#{$1}#{new_comment.lstrip}"
+
+ new_comment = RDoc::Comment.new new_comment, @top_level
+
+ con = RDoc::Constant.new const_name, new_definition, new_comment
+ else
+ con = RDoc::Constant.new const_name, definition, comment
+ end
+ else
+ con = RDoc::Constant.new const_name, definition, comment
+ end
+
+ con.record_location @top_level
+ @stats.add_constant con
+ class_obj.add_constant con
+ end
+
+ ##
+ # Removes #ifdefs that would otherwise confuse us
+
+ def handle_ifdefs_in(body)
+ body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
+ end
+
+ ##
+ # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned
+ # to +var_name+. +type+ is the type of method definition function used.
+ # +singleton_method+ and +module_function+ create a singleton method.
+
+ def handle_method(type, var_name, meth_name, function, param_count,
+ source_file = nil)
+ class_name = @known_classes[var_name]
+ singleton = @singleton_classes.key? var_name
+
+ @methods[var_name][function] << meth_name
+
+ return unless class_name
+
+ class_obj = find_class var_name, class_name
+
+ if class_obj then
+ if meth_name == 'initialize' then
+ meth_name = 'new'
+ singleton = true
+ type = 'method' # force public
+ end
+
+ meth_obj = RDoc::AnyMethod.new '', meth_name
+ meth_obj.c_function = function
+ meth_obj.singleton =
+ singleton || %w[singleton_method module_function].include?(type)
+
+ p_count = Integer(param_count) rescue -1
+
+ if source_file then
+ file_name = File.join @file_dir, source_file
+
+ if File.exist? file_name then
+ file_content = File.read file_name
+ else
+ @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}"
+ end
+ else
+ file_content = @content
+ end
+
+ body = find_body class_name, function, meth_obj, file_content
+
+ if body and meth_obj.document_self then
+ meth_obj.params = if p_count < -1 then # -2 is Array
+ '(*args)'
+ elsif p_count == -1 then # argc, argv
+ rb_scan_args body
+ else
+ "(#{(1..p_count).map { |i| "p#{i}" }.join ', '})"
+ end
+
+
+ meth_obj.record_location @top_level
+ class_obj.add_method meth_obj
+ @stats.add_method meth_obj
+ meth_obj.visibility = :private if 'private_method' == type
+ end
+ end
+ end
+
+ ##
+ # Registers a singleton class +sclass_var+ as a singleton of +class_var+
+
+ def handle_singleton sclass_var, class_var
+ class_name = @known_classes[class_var]
+
+ @known_classes[sclass_var] = class_name
+ @singleton_classes[sclass_var] = class_name
+ end
+
+ ##
+ # Normalizes tabs in +body+
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) do
+ ' ' * (tab_width * $&.length - $`.length % tab_width)
+ end && $~
+ line
+ end.join "\n"
+ else
+ body
+ end
+ end
+
+ ##
+ # Loads the variable map with the given +name+ from the RDoc::Store, if
+ # present.
+
+ def load_variable_map map_name
+ return {} unless files = @store.cache[map_name]
+ return {} unless name_map = files[@file_name]
+
+ class_map = {}
+
+ name_map.each do |variable, name|
+ next unless mod = @store.find_class_or_module(name)
+
+ class_map[variable] = if map_name == :c_class_variables then
+ mod
+ else
+ name
+ end
+ @known_classes[variable] = name
+ end
+
+ class_map
+ end
+
+ ##
+ # Look for directives in a normal comment block:
+ #
+ # /*
+ # * :title: My Awesome Project
+ # */
+ #
+ # This method modifies the +comment+
+
+ def look_for_directives_in context, comment
+ @preprocess.handle comment, context do |directive, param|
+ case directive
+ when 'main' then
+ @options.main_page = param
+ ''
+ when 'title' then
+ @options.default_title = param if @options.respond_to? :default_title=
+ ''
+ end
+ end
+
+ comment
+ end
+
+ ##
+ # Extracts parameters from the +method_body+ and returns a method
+ # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT
+
+ def rb_scan_args method_body
+ method_body =~ /rb_scan_args\((.*?)\)/m
+ return '(*args)' unless $1
+
+ $1.split(/,/)[2] =~ /"(.*?)"/ # format argument
+ format = $1.split(//)
+
+ lead = opt = trail = 0
+
+ if format.first =~ /\d/ then
+ lead = $&.to_i
+ format.shift
+ if format.first =~ /\d/ then
+ opt = $&.to_i
+ format.shift
+ if format.first =~ /\d/ then
+ trail = $&.to_i
+ format.shift
+ block_arg = true
+ end
+ end
+ end
+
+ if format.first == '*' and not block_arg then
+ var = true
+ format.shift
+ if format.first =~ /\d/ then
+ trail = $&.to_i
+ format.shift
+ end
+ end
+
+ if format.first == ':' then
+ hash = true
+ format.shift
+ end
+
+ if format.first == '&' then
+ block = true
+ format.shift
+ end
+
+ # if the format string is not empty there's a bug in the C code, ignore it
+
+ args = []
+ position = 1
+
+ (1...(position + lead)).each do |index|
+ args << "p#{index}"
+ end
+
+ position += lead
+
+ (position...(position + opt)).each do |index|
+ args << "p#{index} = v#{index}"
+ end
+
+ position += opt
+
+ if var then
+ args << '*args'
+ position += 1
+ end
+
+ (position...(position + trail)).each do |index|
+ args << "p#{index}"
+ end
+
+ position += trail
+
+ if hash then
+ args << "p#{position} = {}"
+ end
+
+ args << '&block' if block
+
+ "(#{args.join ', '})"
+ end
+
+ ##
+ # Removes lines that are commented out that might otherwise get picked up
+ # when scanning for classes and methods
+
+ def remove_commented_out_lines
+ @content = @content.gsub(%r%//.*rb_define_%, '//')
+ end
+
+ ##
+ # Extracts the classes, modules, methods, attributes, constants and aliases
+ # from a C file and returns an RDoc::TopLevel for this file
+
+ def scan
+ remove_commented_out_lines
+
+ do_modules
+ do_classes
+ do_missing
+
+ do_constants
+ do_methods
+ do_includes
+ do_aliases
+ do_attrs
+
+ deduplicate_call_seq
+
+ @store.add_c_variables self
+
+ @top_level
+ end
+
+end
+
diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb
new file mode 100644
index 0000000000..167892f543
--- /dev/null
+++ b/lib/rdoc/parser/changelog.rb
@@ -0,0 +1,204 @@
+# frozen_string_literal: true
+require 'time'
+
+##
+# A ChangeLog file parser.
+#
+# This parser converts a ChangeLog into an RDoc::Markup::Document. When
+# viewed as HTML a ChangeLog page will have an entry for each day's entries in
+# the sidebar table of contents.
+#
+# This parser is meant to parse the MRI ChangeLog, but can be used to parse any
+# {GNU style Change
+# Log}[http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html].
+
+class RDoc::Parser::ChangeLog < RDoc::Parser
+
+ include RDoc::Parser::Text
+
+ parse_files_matching(/(\/|\\|\A)ChangeLog[^\/\\]*\z/)
+
+ ##
+ # Attaches the +continuation+ of the previous line to the +entry_body+.
+ #
+ # Continued function listings are joined together as a single entry.
+ # Continued descriptions are joined to make a single paragraph.
+
+ def continue_entry_body entry_body, continuation
+ return unless last = entry_body.last
+
+ if last =~ /\)\s*\z/ and continuation =~ /\A\(/ then
+ last.sub!(/\)\s*\z/, ',')
+ continuation = continuation.sub(/\A\(/, '')
+ end
+
+ if last =~ /\s\z/ then
+ last << continuation
+ else
+ last << ' ' + continuation
+ end
+ end
+
+ ##
+ # Creates an RDoc::Markup::Document given the +groups+ of ChangeLog entries.
+
+ def create_document groups
+ doc = RDoc::Markup::Document.new
+ doc.omit_headings_below = 2
+ doc.file = @top_level
+
+ doc << RDoc::Markup::Heading.new(1, File.basename(@file_name))
+ doc << RDoc::Markup::BlankLine.new
+
+ groups.sort_by do |day,| day end.reverse_each do |day, entries|
+ doc << RDoc::Markup::Heading.new(2, day.dup)
+ doc << RDoc::Markup::BlankLine.new
+
+ doc.concat create_entries entries
+ end
+
+ doc
+ end
+
+ ##
+ # Returns a list of ChangeLog entries an RDoc::Markup nodes for the given
+ # +entries+.
+
+ def create_entries entries
+ out = []
+
+ entries.each do |entry, items|
+ out << RDoc::Markup::Heading.new(3, entry)
+ out << RDoc::Markup::BlankLine.new
+
+ out << create_items(items)
+ end
+
+ out
+ end
+
+ ##
+ # Returns an RDoc::Markup::List containing the given +items+ in the
+ # ChangeLog
+
+ def create_items items
+ list = RDoc::Markup::List.new :NOTE
+
+ items.each do |item|
+ item =~ /\A(.*?(?:\([^)]+\))?):\s*/
+
+ title = $1
+ body = $'
+
+ paragraph = RDoc::Markup::Paragraph.new body
+ list_item = RDoc::Markup::ListItem.new title, paragraph
+ list << list_item
+ end
+
+ list
+ end
+
+ ##
+ # Groups +entries+ by date.
+
+ def group_entries entries
+ @time_cache ||= {}
+ entries.group_by do |title, _|
+ begin
+ time = @time_cache[title]
+ (time || Time.parse(title)).strftime '%Y-%m-%d'
+ rescue NoMethodError, ArgumentError
+ time, = title.split ' ', 2
+ Time.parse(time).strftime '%Y-%m-%d'
+ end
+ end
+ end
+
+ ##
+ # Parses the entries in the ChangeLog.
+ #
+ # Returns an Array of each ChangeLog entry in order of parsing.
+ #
+ # A ChangeLog entry is an Array containing the ChangeLog title (date and
+ # committer) and an Array of ChangeLog items (file and function changed with
+ # description).
+ #
+ # An example result would be:
+ #
+ # [ 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
+ # [ 'README.EXT: Converted to RDoc format',
+ # 'README.EXT.ja: ditto']]
+
+ def parse_entries
+ @time_cache ||= {}
+ entries = []
+ entry_name = nil
+ entry_body = []
+
+ @content.each_line do |line|
+ case line
+ when /^\s*$/ then
+ next
+ when /^\w.*/ then
+ entries << [entry_name, entry_body] if entry_name
+
+ entry_name = $&
+
+ begin
+ time = Time.parse entry_name
+ @time_cache[entry_name] = time
+ # HACK Ruby 1.8 does not raise ArgumentError for Time.parse "Other"
+ entry_name = nil unless entry_name =~ /#{time.year}/
+ rescue NoMethodError
+ # HACK Ruby 2.1.2 and earlier raises NoMethodError if time part is absent
+ entry_name.split ' ', 2
+ rescue ArgumentError
+ if /out of range/ =~ $!.message
+ Time.parse(entry_name.split(' ', 2)[0]) rescue entry_name = nil
+ else
+ entry_name = nil
+ end
+ end
+
+ entry_body = []
+ when /^(\t| {8})?\*\s*(.*)/ then # "\t* file.c (func): ..."
+ entry_body << $2.dup
+ when /^(\t| {8})?\s*(\(.*)/ then # "\t(func): ..."
+ entry = $2
+
+ if entry_body.last =~ /:/ then
+ entry_body << entry.dup
+ else
+ continue_entry_body entry_body, entry
+ end
+ when /^(\t| {8})?\s*(.*)/ then
+ continue_entry_body entry_body, $2
+ end
+ end
+
+ entries << [entry_name, entry_body] if entry_name
+
+ entries.reject! do |(entry,_)|
+ entry == nil
+ end
+
+ entries
+ end
+
+ ##
+ # Converts the ChangeLog into an RDoc::Markup::Document
+
+ def scan
+ @time_cache = {}
+ entries = parse_entries
+ grouped_entries = group_entries entries
+
+ doc = create_document grouped_entries
+
+ @top_level.comment = doc
+
+ @top_level
+ end
+
+end
+
diff --git a/lib/rdoc/parser/markdown.rb b/lib/rdoc/parser/markdown.rb
new file mode 100644
index 0000000000..9ff478f872
--- /dev/null
+++ b/lib/rdoc/parser/markdown.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+##
+# Parse a Markdown format file. The parsed RDoc::Markup::Document is attached
+# as a file comment.
+
+class RDoc::Parser::Markdown < RDoc::Parser
+
+ include RDoc::Parser::Text
+
+ parse_files_matching(/\.(md|markdown)(?:\.[^.]+)?$/)
+
+ ##
+ # Creates an Markdown-format TopLevel for the given file.
+
+ def scan
+ comment = RDoc::Comment.new @content, @top_level
+ comment.format = 'markdown'
+
+ @top_level.comment = comment
+ end
+
+end
+
+
diff --git a/lib/rdoc/parser/rd.rb b/lib/rdoc/parser/rd.rb
new file mode 100644
index 0000000000..25f5711731
--- /dev/null
+++ b/lib/rdoc/parser/rd.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+##
+# Parse a RD format file. The parsed RDoc::Markup::Document is attached as a
+# file comment.
+
+class RDoc::Parser::RD < RDoc::Parser
+
+ include RDoc::Parser::Text
+
+ parse_files_matching(/\.rd(?:\.[^.]+)?$/)
+
+ ##
+ # Creates an rd-format TopLevel for the given file.
+
+ def scan
+ comment = RDoc::Comment.new @content, @top_level
+ comment.format = 'rd'
+
+ @top_level.comment = comment
+ end
+
+end
+
diff --git a/lib/rdoc/parser/ripper_state_lex.rb b/lib/rdoc/parser/ripper_state_lex.rb
new file mode 100644
index 0000000000..b7cec84bfc
--- /dev/null
+++ b/lib/rdoc/parser/ripper_state_lex.rb
@@ -0,0 +1,605 @@
+require 'ripper'
+
+class RDoc::RipperStateLex
+ # TODO: Remove this constants after Ruby 2.4 EOL
+ RIPPER_HAS_LEX_STATE = Ripper::Filter.method_defined?(:state)
+
+ EXPR_NONE = 0
+ EXPR_BEG = 1
+ EXPR_END = 2
+ EXPR_ENDARG = 4
+ EXPR_ENDFN = 8
+ EXPR_ARG = 16
+ EXPR_CMDARG = 32
+ EXPR_MID = 64
+ EXPR_FNAME = 128
+ EXPR_DOT = 256
+ EXPR_CLASS = 512
+ EXPR_LABEL = 1024
+ EXPR_LABELED = 2048
+ EXPR_FITEM = 4096
+ EXPR_VALUE = EXPR_BEG
+ EXPR_BEG_ANY = (EXPR_BEG | EXPR_MID | EXPR_CLASS)
+ EXPR_ARG_ANY = (EXPR_ARG | EXPR_CMDARG)
+ EXPR_END_ANY = (EXPR_END | EXPR_ENDARG | EXPR_ENDFN)
+
+ class InnerStateLex < Ripper::Filter
+ attr_accessor :lex_state
+
+ def initialize(code)
+ @lex_state = EXPR_BEG
+ @in_fname = false
+ @continue = false
+ reset
+ super(code)
+ end
+
+ def reset
+ @command_start = false
+ @cmd_state = @command_start
+ end
+
+ def on_nl(tok, data)
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @continue = true
+ else
+ @continue = false
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_ignored_nl(tok, data)
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @continue = true
+ else
+ @continue = false
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_op(tok, data)
+ case tok
+ when '&', '|', '!', '!=', '!~'
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ when '<<'
+ # TODO next token?
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ when '?'
+ @lex_state = EXPR_BEG
+ when '&&', '||', '+=', '-=', '*=', '**=',
+ '&=', '|=', '^=', '<<=', '>>=', '||=', '&&='
+ @lex_state = EXPR_BEG
+ else
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_kw(tok, data)
+ case tok
+ when 'class'
+ @lex_state = EXPR_CLASS
+ @in_fname = true
+ when 'def'
+ @lex_state = EXPR_FNAME
+ @continue = true
+ @in_fname = true
+ when 'if', 'unless', 'while', 'until'
+ if ((EXPR_END | EXPR_ENDARG | EXPR_ENDFN | EXPR_ARG | EXPR_CMDARG) & @lex_state) != 0 # postfix if
+ @lex_state = EXPR_BEG | EXPR_LABEL
+ else
+ @lex_state = EXPR_BEG
+ end
+ when 'begin'
+ @lex_state = EXPR_BEG
+ else
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_tstring_beg(tok, data)
+ @lex_state = EXPR_BEG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_tstring_end(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_CHAR(tok, data)
+ @lex_state = EXPR_END
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_period(tok, data)
+ @lex_state = EXPR_DOT
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_int(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_float(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_rational(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_imaginary(tok, data)
+ @lex_state = EXPR_END | EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_symbeg(tok, data)
+ @lex_state = EXPR_FNAME
+ @continue = true
+ @in_fname = true
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ private def on_variables(event, tok, data)
+ if @in_fname
+ @lex_state = EXPR_ENDFN
+ @in_fname = false
+ @continue = false
+ elsif @continue
+ case @lex_state
+ when EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_ENDFN
+ @continue = false
+ end
+ else
+ @lex_state = EXPR_CMDARG
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => event, :text => tok, :state => @lex_state})
+ end
+
+ def on_ident(tok, data)
+ on_variables(__method__, tok, data)
+ end
+
+ def on_ivar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_cvar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_gvar(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_backref(tok, data)
+ @lex_state = EXPR_END
+ on_variables(__method__, tok, data)
+ end
+
+ def on_lparen(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_rparen(tok, data)
+ @lex_state = EXPR_ENDFN
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_lbrace(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_rbrace(tok, data)
+ @lex_state = EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_lbracket(tok, data)
+ @lex_state = EXPR_LABEL | EXPR_BEG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_rbracket(tok, data)
+ @lex_state = EXPR_ENDARG
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_const(tok, data)
+ case @lex_state
+ when EXPR_FNAME
+ @lex_state = EXPR_ENDFN
+ when EXPR_CLASS
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_CMDARG
+ end
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_sp(tok, data)
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_comma(tok, data)
+ @lex_state = EXPR_BEG | EXPR_LABEL if (EXPR_ARG_ANY & @lex_state) != 0
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_comment(tok, data)
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_ignored_sp(tok, data)
+ @lex_state = EXPR_BEG unless (EXPR_LABEL & @lex_state) != 0
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ end
+
+ def on_heredoc_end(tok, data)
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => __method__, :text => tok, :state => @lex_state})
+ @lex_state = EXPR_BEG
+ end
+
+ def on_default(event, tok, data)
+ reset
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => event, :text => tok, :state => @lex_state})
+ end
+
+ def each(&block)
+ @callback = block
+ parse
+ end
+ end unless RIPPER_HAS_LEX_STATE
+
+ class InnerStateLex < Ripper::Filter
+ def initialize(code)
+ super(code)
+ end
+
+ def on_default(event, tok, data)
+ @callback.call({ :line_no => lineno, :char_no => column, :kind => event, :text => tok, :state => state})
+ end
+
+ def each(&block)
+ @callback = block
+ parse
+ end
+ end if RIPPER_HAS_LEX_STATE
+
+ def get_squashed_tk
+ if @buf.empty?
+ tk = @inner_lex_enumerator.next
+ else
+ tk = @buf.shift
+ end
+ case tk[:kind]
+ when :on_symbeg then
+ tk = get_symbol_tk(tk)
+ when :on_tstring_beg then
+ tk = get_string_tk(tk)
+ when :on_backtick then
+ if (tk[:state] & (EXPR_FNAME | EXPR_ENDFN)) != 0
+ @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE
+ tk[:kind] = :on_ident
+ tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG
+ else
+ tk = get_string_tk(tk)
+ end
+ when :on_regexp_beg then
+ tk = get_regexp_tk(tk)
+ when :on_embdoc_beg then
+ tk = get_embdoc_tk(tk)
+ when :on_heredoc_beg then
+ @heredoc_queue << retrieve_heredoc_info(tk)
+ @inner_lex.lex_state = EXPR_END unless RIPPER_HAS_LEX_STATE
+ when :on_nl, :on_ignored_nl, :on_comment, :on_heredoc_end then
+ unless @heredoc_queue.empty?
+ get_heredoc_tk(*@heredoc_queue.shift)
+ end
+ when :on_words_beg then
+ tk = get_words_tk(tk)
+ when :on_qwords_beg then
+ tk = get_words_tk(tk)
+ when :on_symbols_beg then
+ tk = get_words_tk(tk)
+ when :on_qsymbols_beg then
+ tk = get_words_tk(tk)
+ when :on_op then
+ if '&.' == tk[:text]
+ tk[:kind] = :on_period
+ else
+ tk = get_op_tk(tk)
+ end
+ end
+ tk
+ end
+
+ private def get_symbol_tk(tk)
+ is_symbol = true
+ symbol_tk = { :line_no => tk[:line_no], :char_no => tk[:char_no], :kind => :on_symbol }
+ if ":'" == tk[:text] or ':"' == tk[:text]
+ tk1 = get_string_tk(tk)
+ symbol_tk[:text] = tk1[:text]
+ symbol_tk[:state] = tk1[:state]
+ else
+ case (tk1 = get_squashed_tk)[:kind]
+ when :on_ident
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_tstring_content
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = get_squashed_tk[:state] # skip :on_tstring_end
+ when :on_tstring_end
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_op
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_ivar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_cvar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_gvar
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_const
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ when :on_kw
+ symbol_tk[:text] = ":#{tk1[:text]}"
+ symbol_tk[:state] = tk1[:state]
+ else
+ is_symbol = false
+ tk = tk1
+ end
+ end
+ if is_symbol
+ tk = symbol_tk
+ end
+ tk
+ end
+
+ private def get_string_tk(tk)
+ string = tk[:text]
+ state = nil
+ kind = :on_tstring
+ loop do
+ inner_str_tk = get_squashed_tk
+ if inner_str_tk.nil?
+ break
+ elsif :on_tstring_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ break
+ elsif :on_label_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ kind = :on_symbol
+ break
+ else
+ string = string + inner_str_tk[:text]
+ if :on_embexpr_beg == inner_str_tk[:kind] then
+ kind = :on_dstring if :on_tstring == kind
+ end
+ end
+ end
+ {
+ :line_no => tk[:line_no],
+ :char_no => tk[:char_no],
+ :kind => kind,
+ :text => string,
+ :state => state
+ }
+ end
+
+ private def get_regexp_tk(tk)
+ string = tk[:text]
+ state = nil
+ loop do
+ inner_str_tk = get_squashed_tk
+ if inner_str_tk.nil?
+ break
+ elsif :on_regexp_end == inner_str_tk[:kind]
+ string = string + inner_str_tk[:text]
+ state = inner_str_tk[:state]
+ break
+ else
+ string = string + inner_str_tk[:text]
+ end
+ end
+ {
+ :line_no => tk[:line_no],
+ :char_no => tk[:char_no],
+ :kind => :on_regexp,
+ :text => string,
+ :state => state
+ }
+ end
+
+ private def get_embdoc_tk(tk)
+ string = tk[:text]
+ until :on_embdoc_end == (embdoc_tk = get_squashed_tk)[:kind] do
+ string = string + embdoc_tk[:text]
+ end
+ string = string + embdoc_tk[:text]
+ {
+ :line_no => tk[:line_no],
+ :char_no => tk[:char_no],
+ :kind => :on_embdoc,
+ :text => string,
+ :state => embdoc_tk[:state]
+ }
+ end
+
+ private def get_heredoc_tk(heredoc_name, indent)
+ string = ''
+ start_tk = nil
+ prev_tk = nil
+ until heredoc_end?(heredoc_name, indent, tk = @inner_lex_enumerator.next) do
+ start_tk = tk unless start_tk
+ if (prev_tk.nil? or "\n" == prev_tk[:text][-1]) and 0 != tk[:char_no]
+ string = string + (' ' * tk[:char_no])
+ end
+ string = string + tk[:text]
+ prev_tk = tk
+ end
+ start_tk = tk unless start_tk
+ prev_tk = tk unless prev_tk
+ @buf.unshift tk # closing heredoc
+ heredoc_tk = {
+ :line_no => start_tk[:line_no],
+ :char_no => start_tk[:char_no],
+ :kind => :on_heredoc,
+ :text => string,
+ :state => prev_tk[:state]
+ }
+ @buf.unshift heredoc_tk
+ end
+
+ private def retrieve_heredoc_info(tk)
+ name = tk[:text].gsub(/\A<<[-~]?(['"`]?)(.+)\1\z/, '\2')
+ indent = tk[:text] =~ /\A<<[-~]/
+ [name, indent]
+ end
+
+ private def heredoc_end?(name, indent, tk)
+ result = false
+ if :on_heredoc_end == tk[:kind] then
+ tk_name = (indent ? tk[:text].gsub(/^ *(.+)\n?$/, '\1') : tk[:text].gsub(/\n\z/, ''))
+ if name == tk_name
+ result = true
+ end
+ end
+ result
+ end
+
+ private def get_words_tk(tk)
+ string = ''
+ start_token = tk[:text]
+ start_quote = tk[:text].rstrip[-1]
+ line_no = tk[:line_no]
+ char_no = tk[:char_no]
+ state = tk[:state]
+ end_quote =
+ case start_quote
+ when ?( then ?)
+ when ?[ then ?]
+ when ?{ then ?}
+ when ?< then ?>
+ else start_quote
+ end
+ end_token = nil
+ loop do
+ tk = get_squashed_tk
+ if tk.nil?
+ end_token = end_quote
+ break
+ elsif :on_tstring_content == tk[:kind] then
+ string += tk[:text]
+ elsif :on_words_sep == tk[:kind] or :on_tstring_end == tk[:kind] then
+ if end_quote == tk[:text].strip then
+ end_token = tk[:text]
+ break
+ else
+ string += tk[:text]
+ end
+ else
+ string += tk[:text]
+ end
+ end
+ text = "#{start_token}#{string}#{end_token}"
+ {
+ :line_no => line_no,
+ :char_no => char_no,
+ :kind => :on_dstring,
+ :text => text,
+ :state => state
+ }
+ end
+
+ private def get_op_tk(tk)
+ redefinable_operators = %w[! != !~ % & * ** + +@ - -@ / < << <= <=> == === =~ > >= >> [] []= ^ ` | ~]
+ if redefinable_operators.include?(tk[:text]) and tk[:state] == EXPR_ARG then
+ @inner_lex.lex_state = EXPR_ARG unless RIPPER_HAS_LEX_STATE
+ tk[:state] = Ripper::Lexer.const_defined?(:State) ? Ripper::Lexer::State.new(EXPR_ARG) : EXPR_ARG
+ tk[:kind] = :on_ident
+ elsif tk[:text] =~ /^[-+]$/ then
+ tk_ahead = get_squashed_tk
+ case tk_ahead[:kind]
+ when :on_int, :on_float, :on_rational, :on_imaginary then
+ tk[:text] += tk_ahead[:text]
+ tk[:kind] = tk_ahead[:kind]
+ tk[:state] = tk_ahead[:state]
+ else
+ @buf.unshift tk_ahead
+ end
+ end
+ tk
+ end
+
+ def initialize(code)
+ @buf = []
+ @heredoc_queue = []
+ @inner_lex = InnerStateLex.new(code)
+ @inner_lex_enumerator = Enumerator.new do |y|
+ @inner_lex.each do |tk|
+ y << tk
+ end
+ end
+ end
+
+ def self.parse(code)
+ lex = self.new(code)
+ tokens = []
+ begin
+ while tk = lex.get_squashed_tk
+ tokens.push tk
+ end
+ rescue StopIteration
+ end
+ tokens
+ end
+
+ def self.end?(token)
+ (token[:state] & EXPR_END)
+ end
+end
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
new file mode 100644
index 0000000000..8599f655ad
--- /dev/null
+++ b/lib/rdoc/parser/ruby.rb
@@ -0,0 +1,2232 @@
+# frozen_string_literal: true
+##
+# This file contains stuff stolen outright from:
+#
+# rtags.rb -
+# ruby-lex.rb - ruby lexcal analyzer
+# ruby-token.rb - ruby tokens
+# by Keiju ISHITSUKA (Nippon Rational Inc.)
+#
+
+$TOKEN_DEBUG ||= nil
+
+##
+# Extracts code elements from a source file returning a TopLevel object
+# containing the constituent file elements.
+#
+# This file is based on rtags
+#
+# RubyParser understands how to document:
+# * classes
+# * modules
+# * methods
+# * constants
+# * aliases
+# * private, public, protected
+# * private_class_function, public_class_function
+# * private_constant, public_constant
+# * module_function
+# * attr, attr_reader, attr_writer, attr_accessor
+# * extra accessors given on the command line
+# * metaprogrammed methods
+# * require
+# * include
+#
+# == Method Arguments
+#
+#--
+# NOTE: I don't think this works, needs tests, remove the paragraph following
+# this block when known to work
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :args: directive:
+#
+# ##
+# # This method tries over and over until it is tired
+#
+# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
+# puts thing_to_try
+# go_go_go thing_to_try, tries - 1
+# end
+#
+# If you have a more-complex set of overrides you can use the :call-seq:
+# directive:
+#++
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :call-seq:
+# directive:
+#
+# ##
+# # This method can be called with a range or an offset and length
+# #
+# # :call-seq:
+# # my_method(Range)
+# # my_method(offset, length)
+#
+# def my_method(*args)
+# end
+#
+# The parser extracts +yield+ expressions from method bodies to gather the
+# yielded argument names. If your method manually calls a block instead of
+# yielding or you want to override the discovered argument names use
+# the :yields: directive:
+#
+# ##
+# # My method is awesome
+#
+# def my_method(&block) # :yields: happy, times
+# block.call 1, 2
+# end
+#
+# == Metaprogrammed Methods
+#
+# To pick up a metaprogrammed method, the parser looks for a comment starting
+# with '##' before an identifier:
+#
+# ##
+# # This is a meta-programmed method!
+#
+# add_my_method :meta_method, :arg1, :arg2
+#
+# The parser looks at the token after the identifier to determine the name, in
+# this example, :meta_method. If a name cannot be found, a warning is printed
+# and 'unknown is used.
+#
+# You can force the name of a method using the :method: directive:
+#
+# ##
+# # :method: some_method!
+#
+# By default, meta-methods are instance methods. To indicate that a method is
+# a singleton method instead use the :singleton-method: directive:
+#
+# ##
+# # :singleton-method:
+#
+# You can also use the :singleton-method: directive with a name:
+#
+# ##
+# # :singleton-method: some_method!
+#
+# You can define arguments for metaprogrammed methods via either the
+# :call-seq:, :arg: or :args: directives.
+#
+# Additionally you can mark a method as an attribute by
+# using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like
+# for :method:, the name is optional.
+#
+# ##
+# # :attr_reader: my_attr_name
+#
+# == Hidden methods and attributes
+#
+# You can provide documentation for methods that don't appear using
+# the :method:, :singleton-method: and :attr: directives:
+#
+# ##
+# # :attr_writer: ghost_writer
+# # There is an attribute here, but you can't see it!
+#
+# ##
+# # :method: ghost_method
+# # There is a method here, but you can't see it!
+#
+# ##
+# # this is a comment for a regular method
+#
+# def regular_method() end
+#
+# Note that by default, the :method: directive will be ignored if there is a
+# standard rdocable item following it.
+
+require 'ripper'
+
+class RDoc::Parser::Ruby < RDoc::Parser
+
+ parse_files_matching(/\.rbw?$/)
+
+ include RDoc::TokenStream
+ include RDoc::Parser::RubyTools
+
+ ##
+ # RDoc::NormalClass type
+
+ NORMAL = "::"
+
+ ##
+ # RDoc::SingleClass type
+
+ SINGLE = "<<"
+
+ ##
+ # Creates a new Ruby parser.
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ if /\t/ =~ content then
+ tab_width = @options.tab_width
+ content = content.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) {
+ ' ' * (tab_width*$&.length - $`.length % tab_width)
+ } && $~
+ line
+ end.join("\n")
+ end
+
+ @size = 0
+ @token_listeners = nil
+ @scanner = RDoc::RipperStateLex.parse(content)
+ @content = content
+ @scanner_point = 0
+ @prev_seek = nil
+ @markup = @options.markup
+ @track_visibility = :nodoc != @options.visibility
+ @encoding = @options.encoding
+
+ reset
+ end
+
+ def tk_nl?(tk)
+ :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
+ end
+
+ ##
+ # Retrieves the read token stream and replaces +pattern+ with +replacement+
+ # using gsub. If the result is only a ";" returns an empty string.
+
+ def get_tkread_clean pattern, replacement # :nodoc:
+ read = get_tkread.gsub(pattern, replacement).strip
+ return '' if read == ';'
+ read
+ end
+
+ ##
+ # Extracts the visibility information for the visibility token +tk+
+ # and +single+ class type identifier.
+ #
+ # Returns the visibility type (a string), the visibility (a symbol) and
+ # +singleton+ if the methods following should be converted to singleton
+ # methods.
+
+ def get_visibility_information tk, single # :nodoc:
+ vis_type = tk[:text]
+ singleton = single == SINGLE
+
+ vis =
+ case vis_type
+ when 'private' then :private
+ when 'protected' then :protected
+ when 'public' then :public
+ when 'private_class_method' then
+ singleton = true
+ :private
+ when 'public_class_method' then
+ singleton = true
+ :public
+ when 'module_function' then
+ singleton = true
+ :public
+ else
+ raise RDoc::Error, "Invalid visibility: #{tk.name}"
+ end
+
+ return vis_type, vis, singleton
+ end
+
+ ##
+ # Look for the first comment in a file that isn't a shebang line.
+
+ def collect_first_comment
+ skip_tkspace
+ comment = ''.dup
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
+ first_line = true
+ first_comment_tk_kind = nil
+
+ tk = get_tk
+
+ while tk && (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
+ if first_line and tk[:text] =~ /\A#!/ then
+ skip_tkspace
+ tk = get_tk
+ elsif first_line and tk[:text] =~ /\A#\s*-\*-/ then
+ first_line = false
+ skip_tkspace
+ tk = get_tk
+ else
+ break if first_comment_tk_kind and not first_comment_tk_kind === tk[:kind]
+ first_comment_tk_kind = tk[:kind]
+
+ first_line = false
+ comment << tk[:text]
+ tk = get_tk
+
+ if :on_nl === tk then
+ skip_tkspace false
+ tk = get_tk
+ end
+ end
+ end
+
+ unget_tk tk
+
+ new_comment comment
+ end
+
+ ##
+ # Consumes trailing whitespace from the token stream
+
+ def consume_trailing_spaces # :nodoc:
+ skip_tkspace false
+ end
+
+ ##
+ # Creates a new attribute in +container+ with +name+.
+
+ def create_attr container, single, name, rw, comment # :nodoc:
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ record_location att
+
+ container.add_attribute att
+ @stats.add_attribute att
+
+ att
+ end
+
+ ##
+ # Creates a module alias in +container+ at +rhs_name+ (or at the top-level
+ # for "::") with the name from +constant+.
+
+ def create_module_alias container, constant, rhs_name # :nodoc:
+ mod = if rhs_name =~ /^::/ then
+ @store.find_class_or_module rhs_name
+ else
+ container.find_module_named rhs_name
+ end
+
+ container.add_module_alias mod, constant.name, @top_level if mod
+ end
+
+ ##
+ # Aborts with +msg+
+
+ def error(msg)
+ msg = make_message msg
+
+ abort msg
+ end
+
+ ##
+ # Looks for a true or false token.
+
+ def get_bool
+ skip_tkspace
+ tk = get_tk
+ if :on_kw == tk[:kind] && 'true' == tk[:text]
+ true
+ elsif :on_kw == tk[:kind] && ('false' == tk[:text] || 'nil' == tk[:text])
+ false
+ else
+ unget_tk tk
+ true
+ end
+ end
+
+ ##
+ # Look for the name of a class of module (optionally with a leading :: or
+ # with :: separated named) and return the ultimate name, the associated
+ # container, and the given name (with the ::).
+
+ def get_class_or_module container, ignore_constants = false
+ skip_tkspace
+ name_t = get_tk
+ given_name = ''.dup
+
+ # class ::A -> A is in the top level
+ if :on_op == name_t[:kind] and '::' == name_t[:text] then # bug
+ name_t = get_tk
+ container = @top_level
+ given_name << '::'
+ end
+
+ skip_tkspace false
+ given_name << name_t[:text]
+
+ is_self = name_t[:kind] == :on_op && name_t[:text] == '<<'
+ while !is_self && (tk = peek_tk) and :on_op == tk[:kind] and '::' == tk[:text] do
+ prev_container = container
+ container = container.find_module_named name_t[:text]
+ container ||=
+ if ignore_constants then
+ RDoc::Context.new
+ else
+ c = prev_container.add_module RDoc::NormalModule, name_t[:text]
+ c.ignore unless prev_container.document_children
+ @top_level.add_to_classes_or_modules c
+ c
+ end
+
+ record_location container
+
+ get_tk
+ skip_tkspace false
+ name_t = get_tk
+ unless :on_const == name_t[:kind] || :on_ident == name_t[:kind]
+ raise RDoc::Error, "Invalid class or module definition: #{given_name}"
+ end
+ if prev_container == container and !ignore_constants
+ given_name = name_t[:text]
+ else
+ given_name << '::' + name_t[:text]
+ end
+ end
+
+ skip_tkspace false
+
+ return [container, name_t, given_name]
+ end
+
+ ##
+ # Return a superclass, which can be either a constant of an expression
+
+ def get_class_specification
+ tk = peek_tk
+ if tk.nil?
+ return ''
+ elsif :on_kw == tk[:kind] && 'self' == tk[:text]
+ return 'self'
+ elsif :on_gvar == tk[:kind]
+ return ''
+ end
+
+ res = get_constant
+
+ skip_tkspace false
+
+ get_tkread # empty out read buffer
+
+ tk = get_tk
+ return res unless tk
+
+ case tk[:kind]
+ when :on_nl, :on_comment, :on_embdoc, :on_semicolon then
+ unget_tk(tk)
+ return res
+ end
+
+ res += parse_call_parameters(tk)
+ res
+ end
+
+ ##
+ # Parse a constant, which might be qualified by one or more class or module
+ # names
+
+ def get_constant
+ res = ""
+ skip_tkspace false
+ tk = get_tk
+
+ while tk && ((:on_op == tk[:kind] && '::' == tk[:text]) || :on_const == tk[:kind]) do
+ res += tk[:text]
+ tk = get_tk
+ end
+
+ unget_tk(tk)
+ res
+ end
+
+ ##
+ # Get a constant that may be surrounded by parens
+
+ def get_constant_with_optional_parens
+ skip_tkspace false
+
+ nest = 0
+
+ while :on_lparen == (tk = peek_tk)[:kind] do
+ get_tk
+ skip_tkspace
+ nest += 1
+ end
+
+ name = get_constant
+
+ while nest > 0
+ skip_tkspace
+ tk = get_tk
+ nest -= 1 if :on_rparen == tk[:kind]
+ end
+
+ name
+ end
+
+ ##
+ # Little hack going on here. In the statement:
+ #
+ # f = 2*(1+yield)
+ #
+ # We see the RPAREN as the next token, so we need to exit early. This still
+ # won't catch all cases (such as "a = yield + 1"
+
+ def get_end_token tk # :nodoc:
+ case tk[:kind]
+ when :on_lparen
+ {
+ :kind => :on_rparen,
+ :text => ')'
+ }
+ when :on_rparen
+ nil
+ else
+ {
+ :kind => :on_nl,
+ :text => "\n"
+ }
+ end
+ end
+
+ ##
+ # Retrieves the method container for a singleton method.
+
+ def get_method_container container, name_t # :nodoc:
+ prev_container = container
+ container = container.find_module_named(name_t[:text])
+
+ unless container then
+ constant = prev_container.constants.find do |const|
+ const.name == name_t[:text]
+ end
+
+ if constant then
+ parse_method_dummy prev_container
+ return
+ end
+ end
+
+ unless container then
+ # TODO seems broken, should starting at Object in @store
+ obj = name_t[:text].split("::").inject(Object) do |state, item|
+ state.const_get(item)
+ end rescue nil
+
+ type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
+
+ unless [Class, Module].include?(obj.class) then
+ warn("Couldn't find #{name_t[:text]}. Assuming it's a module")
+ end
+
+ if type == RDoc::NormalClass then
+ sclass = obj.superclass ? obj.superclass.name : nil
+ container = prev_container.add_class type, name_t[:text], sclass
+ else
+ container = prev_container.add_module type, name_t[:text]
+ end
+
+ record_location container
+ end
+
+ container
+ end
+
+ ##
+ # Extracts a name or symbol from the token stream.
+
+ def get_symbol_or_name
+ tk = get_tk
+ case tk[:kind]
+ when :on_symbol then
+ text = tk[:text].sub(/^:/, '')
+
+ next_tk = peek_tk
+ if next_tk && :on_op == next_tk[:kind] && '=' == next_tk[:text] then
+ get_tk
+ text << '='
+ end
+
+ text
+ when :on_ident, :on_const, :on_gvar, :on_cvar, :on_ivar, :on_op, :on_kw then
+ tk[:text]
+ when :on_tstring, :on_dstring then
+ tk[:text][1..-2]
+ else
+ raise RDoc::Error, "Name or symbol expected (got #{tk})"
+ end
+ end
+
+ ##
+ # Marks containers between +container+ and +ancestor+ as ignored
+
+ def suppress_parents container, ancestor # :nodoc:
+ while container and container != ancestor do
+ container.suppress unless container.documented?
+ container = container.parent
+ end
+ end
+
+ ##
+ # Look for directives in a normal comment block:
+ #
+ # # :stopdoc:
+ # # Don't display comment from this point forward
+ #
+ # This routine modifies its +comment+ parameter.
+
+ def look_for_directives_in container, comment
+ @preprocess.handle comment, container do |directive, param|
+ case directive
+ when 'method', 'singleton-method',
+ 'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
+ false # handled elsewhere
+ when 'section' then
+ break unless container.kind_of?(RDoc::Context)
+ container.set_current_section param, comment.dup
+ comment.text = ''
+ break
+ end
+ end
+
+ comment.remove_private
+ end
+
+ ##
+ # Adds useful info about the parser to +message+
+
+ def make_message message
+ prefix = "#{@file_name}:".dup
+
+ tk = peek_tk
+ prefix << "#{tk[:line_no]}:#{tk[:char_no]}:" if tk
+
+ "#{prefix} #{message}"
+ end
+
+ ##
+ # Creates a comment with the correct format
+
+ def new_comment comment
+ c = RDoc::Comment.new comment, @top_level
+ c.format = @markup
+ c
+ end
+
+ ##
+ # Creates an RDoc::Attr for the name following +tk+, setting the comment to
+ # +comment+.
+
+ def parse_attr(context, single, tk, comment)
+ line_no = tk[:line_no]
+
+ args = parse_symbol_arg 1
+ if args.size > 0 then
+ name = args[0]
+ rw = "R"
+ skip_tkspace false
+ tk = get_tk
+
+ if :on_comma == tk[:kind] then
+ rw = "RW" if get_bool
+ else
+ unget_tk tk
+ end
+
+ att = create_attr context, single, name, rw, comment
+ att.line = line_no
+
+ read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
+ else
+ warn "'attr' ignored - looks like a variable"
+ end
+ end
+
+ ##
+ # Creates an RDoc::Attr for each attribute listed after +tk+, setting the
+ # comment for each to +comment+.
+
+ def parse_attr_accessor(context, single, tk, comment)
+ line_no = tk[:line_no]
+
+ args = parse_symbol_arg
+ rw = "?"
+
+ tmp = RDoc::CodeObject.new
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
+ # TODO In most other places we let the context keep track of document_self
+ # and add found items appropriately but here we do not. I'm not sure why.
+ return if @track_visibility and not tmp.document_self
+
+ case tk[:text]
+ when "attr_reader" then rw = "R"
+ when "attr_writer" then rw = "W"
+ when "attr_accessor" then rw = "RW"
+ else
+ rw = '?'
+ end
+
+ for name in args
+ att = create_attr context, single, name, rw, comment
+ att.line = line_no
+ end
+ end
+
+ ##
+ # Parses an +alias+ in +context+ with +comment+
+
+ def parse_alias(context, single, tk, comment)
+ line_no = tk[:line_no]
+
+ skip_tkspace
+
+ if :on_lparen === peek_tk[:kind] then
+ get_tk
+ skip_tkspace
+ end
+
+ new_name = get_symbol_or_name
+
+ skip_tkspace
+ if :on_comma === peek_tk[:kind] then
+ get_tk
+ skip_tkspace
+ end
+
+ begin
+ old_name = get_symbol_or_name
+ rescue RDoc::Error
+ return
+ end
+
+ al = RDoc::Alias.new(get_tkread, old_name, new_name, comment,
+ single == SINGLE)
+ record_location al
+ al.line = line_no
+
+ read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
+ context.add_alias al
+ @stats.add_alias al
+
+ al
+ end
+
+ ##
+ # Extracts call parameters from the token stream.
+
+ def parse_call_parameters(tk)
+ end_token = case tk[:kind]
+ when :on_lparen
+ :on_rparen
+ when :on_rparen
+ return ""
+ else
+ :on_nl
+ end
+ nest = 0
+
+ loop do
+ break if tk.nil?
+ case tk[:kind]
+ when :on_semicolon
+ break
+ when :on_lparen
+ nest += 1
+ when end_token
+ if end_token == :on_rparen
+ nest -= 1
+ break if RDoc::RipperStateLex.end?(tk) and nest <= 0
+ else
+ break if RDoc::RipperStateLex.end?(tk)
+ end
+ when :on_comment, :on_embdoc
+ unget_tk(tk)
+ break
+ when :on_op
+ if tk[:text] =~ /^(.{1,2})?=$/
+ unget_tk(tk)
+ break
+ end
+ end
+ tk = get_tk
+ end
+
+ get_tkread_clean "\n", " "
+ end
+
+ ##
+ # Parses a class in +context+ with +comment+
+
+ def parse_class container, single, tk, comment
+ line_no = tk[:line_no]
+
+ declaration_context = container
+ container, name_t, given_name = get_class_or_module container
+
+ if name_t[:kind] == :on_const
+ cls = parse_class_regular container, declaration_context, single,
+ name_t, given_name, comment
+ elsif name_t[:kind] == :on_op && name_t[:text] == '<<'
+ case name = get_class_specification
+ when 'self', container.name
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+ parse_statements container, SINGLE
+ return # don't update line
+ else
+ cls = parse_class_singleton container, name, comment
+ end
+ else
+ warn "Expected class name or '<<'. Got #{name_t[:kind]}: #{name_t[:text].inspect}"
+ return
+ end
+
+ cls.line = line_no
+
+ # after end modifiers
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+
+ cls
+ end
+
+ ##
+ # Parses and creates a regular class
+
+ def parse_class_regular container, declaration_context, single, # :nodoc:
+ name_t, given_name, comment
+ superclass = '::Object'
+
+ if given_name =~ /^::/ then
+ declaration_context = @top_level
+ given_name = $'
+ end
+
+ tk = peek_tk
+ if tk[:kind] == :on_op && tk[:text] == '<' then
+ get_tk
+ skip_tkspace
+ superclass = get_class_specification
+ superclass = '(unknown)' if superclass.empty?
+ end
+
+ cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
+ cls = declaration_context.add_class cls_type, given_name, superclass
+ cls.ignore unless container.document_children
+
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+ record_location cls
+
+ cls.add_comment comment, @top_level
+
+ @top_level.add_to_classes_or_modules cls
+ @stats.add_class cls
+
+ suppress_parents container, declaration_context unless cls.document_self
+
+ parse_statements cls
+
+ cls
+ end
+
+ ##
+ # Parses a singleton class in +container+ with the given +name+ and
+ # +comment+.
+
+ def parse_class_singleton container, name, comment # :nodoc:
+ other = @store.find_class_named name
+
+ unless other then
+ if name =~ /^::/ then
+ name = $'
+ container = @top_level
+ end
+
+ other = container.add_module RDoc::NormalModule, name
+ record_location other
+
+ # class << $gvar
+ other.ignore if name.empty?
+
+ other.add_comment comment, @top_level
+ end
+
+ # notify :nodoc: all if not a constant-named class/module
+ # (and remove any comment)
+ unless name =~ /\A(::)?[A-Z]/ then
+ other.document_self = nil
+ other.document_children = false
+ other.clear_comment
+ end
+
+ @top_level.add_to_classes_or_modules other
+ @stats.add_class other
+
+ read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
+ parse_statements(other, SINGLE)
+
+ other
+ end
+
+ ##
+ # Parses a constant in +context+ with +comment+. If +ignore_constants+ is
+ # true, no found constants will be added to RDoc.
+
+ def parse_constant container, tk, comment, ignore_constants = false
+ line_no = tk[:line_no]
+
+ name = tk[:text]
+ skip_tkspace false
+
+ return unless name =~ /^\w+$/
+
+ if :on_op == peek_tk[:kind] && '::' == peek_tk[:text] then
+ unget_tk tk
+
+ container, name_t, = get_class_or_module container, ignore_constants
+
+ name = name_t[:text]
+ end
+
+ is_array_or_hash = false
+ if peek_tk && :on_lbracket == peek_tk[:kind]
+ get_tk
+ nest = 1
+ while bracket_tk = get_tk
+ case bracket_tk[:kind]
+ when :on_lbracket
+ nest += 1
+ when :on_rbracket
+ nest -= 1
+ break if nest == 0
+ end
+ end
+ skip_tkspace false
+ is_array_or_hash = true
+ end
+
+ unless peek_tk && :on_op == peek_tk[:kind] && '=' == peek_tk[:text] then
+ return false
+ end
+ get_tk
+
+ value = ''
+ con = RDoc::Constant.new name, value, comment
+
+ body = parse_constant_body container, con, is_array_or_hash
+
+ return unless body
+
+ con.value = body
+ record_location con
+ con.line = line_no
+ read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
+
+ return if is_array_or_hash
+
+ @stats.add_constant con
+ container.add_constant con
+
+ true
+ end
+
+ def parse_constant_body container, constant, is_array_or_hash # :nodoc:
+ nest = 0
+ rhs_name = ''.dup
+
+ get_tkread
+
+ tk = get_tk
+
+ body = nil
+ loop do
+ break if tk.nil?
+ if :on_semicolon == tk[:kind] then
+ break if nest <= 0
+ elsif [:on_tlambeg, :on_lparen, :on_lbrace, :on_lbracket].include?(tk[:kind]) then
+ nest += 1
+ elsif (:on_kw == tk[:kind] && 'def' == tk[:text]) then
+ nest += 1
+ elsif (:on_kw == tk[:kind] && %w{do if unless case begin}.include?(tk[:text])) then
+ if (tk[:state] & RDoc::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ end
+ elsif [:on_rparen, :on_rbrace, :on_rbracket].include?(tk[:kind]) ||
+ (:on_kw == tk[:kind] && 'end' == tk[:text]) then
+ nest -= 1
+ elsif (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
+ unget_tk tk
+ if nest <= 0 and RDoc::RipperStateLex.end?(tk) then
+ body = get_tkread_clean(/^[ \t]+/, '')
+ read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
+ break
+ else
+ read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
+ end
+ elsif :on_const == tk[:kind] then
+ rhs_name << tk[:text]
+
+ next_tk = peek_tk
+ if nest <= 0 and (next_tk.nil? || :on_nl == next_tk[:kind]) then
+ create_module_alias container, constant, rhs_name unless is_array_or_hash
+ break
+ end
+ elsif :on_nl == tk[:kind] then
+ if nest <= 0 and RDoc::RipperStateLex.end?(tk) then
+ unget_tk tk
+ break
+ end
+ elsif :on_op == tk[:kind] && '::' == tk[:text]
+ rhs_name << '::'
+ end
+ tk = get_tk
+ end
+
+ body ? body : get_tkread_clean(/^[ \t]+/, '')
+ end
+
+ ##
+ # Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for
+ # :method: or :attr: directives in +comment+.
+
+ def parse_comment container, tk, comment
+ return parse_comment_tomdoc container, tk, comment if @markup == 'tomdoc'
+ column = tk[:char_no]
+ line_no = tk[:line_no]
+
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ singleton = !!$~
+
+ co =
+ if (comment.text = comment.text.sub(/^# +:?method: *(\S*).*?\n/i, '')) && !!$~ then
+ parse_comment_ghost container, comment.text, $1, column, line_no, comment
+ elsif (comment.text = comment.text.sub(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '')) && !!$~ then
+ parse_comment_attr container, $1, $3, comment
+ end
+
+ if co then
+ co.singleton = singleton
+ co.line = line_no
+ end
+
+ true
+ end
+
+ ##
+ # Parse a comment that is describing an attribute in +container+ with the
+ # given +name+ and +comment+.
+
+ def parse_comment_attr container, type, name, comment # :nodoc:
+ return if name.empty?
+
+ rw = case type
+ when 'attr_reader' then 'R'
+ when 'attr_writer' then 'W'
+ else 'RW'
+ end
+
+ create_attr container, NORMAL, name, rw, comment
+ end
+
+ def parse_comment_ghost container, text, name, column, line_no, # :nodoc:
+ comment
+ name = nil if name.empty?
+
+ meth = RDoc::GhostMethod.new get_tkread, name
+ record_location meth
+
+ meth.start_collecting_tokens
+ indent = { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => ' ' * column }
+ position_comment = { :line_no => line_no, :char_no => 1, :kind => :on_comment }
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" }
+ meth.add_tokens [position_comment, newline, indent]
+
+ meth.params =
+ if text.sub!(/^#\s+:?args?:\s*(.*?)\s*$/i, '') then
+ $1
+ else
+ ''
+ end
+
+ comment.normalize
+ comment.extract_call_seq meth
+
+ return unless meth.name
+
+ container.add_method meth
+
+ meth.comment = comment
+
+ @stats.add_method meth
+
+ meth
+ end
+
+ ##
+ # Creates an RDoc::Method on +container+ from +comment+ if there is a
+ # Signature section in the comment
+
+ def parse_comment_tomdoc container, tk, comment
+ return unless signature = RDoc::TomDoc.signature(comment)
+ column = tk[:char_no]
+ line_no = tk[:line_no]
+
+ name, = signature.split %r%[ \(]%, 2
+
+ meth = RDoc::GhostMethod.new get_tkread, name
+ record_location meth
+ meth.line = line_no
+
+ meth.start_collecting_tokens
+ indent = { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => ' ' * column }
+ position_comment = { :line_no => line_no, :char_no => 1, :kind => :on_comment }
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" }
+ meth.add_tokens [position_comment, newline, indent]
+
+ meth.call_seq = signature
+
+ comment.normalize
+
+ return unless meth.name
+
+ container.add_method meth
+
+ meth.comment = comment
+
+ @stats.add_method meth
+ end
+
+ ##
+ # Parses an +include+ or +extend+, indicated by the +klass+ and adds it to
+ # +container+ # with +comment+
+
+ def parse_extend_or_include klass, container, comment # :nodoc:
+ loop do
+ skip_tkspace_comment
+
+ name = get_constant_with_optional_parens
+
+ unless name.empty? then
+ obj = container.add klass, name, comment
+ record_location obj
+ end
+
+ return if peek_tk.nil? || :on_comma != peek_tk[:kind]
+
+ get_tk
+ end
+ end
+
+ ##
+ # Parses identifiers that can create new methods or change visibility.
+ #
+ # Returns true if the comment was not consumed.
+
+ def parse_identifier container, single, tk, comment # :nodoc:
+ case tk[:text]
+ when 'private', 'protected', 'public', 'private_class_method',
+ 'public_class_method', 'module_function' then
+ parse_visibility container, single, tk
+ return true
+ when 'private_constant', 'public_constant'
+ parse_constant_visibility container, single, tk
+ return true
+ when 'attr' then
+ parse_attr container, single, tk, comment
+ when /^attr_(reader|writer|accessor)$/ then
+ parse_attr_accessor container, single, tk, comment
+ when 'alias_method' then
+ parse_alias container, single, tk, comment
+ when 'require', 'include' then
+ # ignore
+ else
+ if comment.text =~ /\A#\#$/ then
+ case comment.text
+ when /^# +:?attr(_reader|_writer|_accessor)?:/ then
+ parse_meta_attr container, single, tk, comment
+ else
+ method = parse_meta_method container, single, tk, comment
+ method.params = container.params if
+ container.params
+ method.block_params = container.block_params if
+ container.block_params
+ end
+ end
+ end
+
+ false
+ end
+
+ ##
+ # Parses a meta-programmed attribute and creates an RDoc::Attr.
+ #
+ # To create foo and bar attributes on class C with comment "My attributes":
+ #
+ # class C
+ #
+ # ##
+ # # :attr:
+ # #
+ # # My attributes
+ #
+ # my_attr :foo, :bar
+ #
+ # end
+ #
+ # To create a foo attribute on class C with comment "My attribute":
+ #
+ # class C
+ #
+ # ##
+ # # :attr: foo
+ # #
+ # # My attribute
+ #
+ # my_attr :foo, :bar
+ #
+ # end
+
+ def parse_meta_attr(context, single, tk, comment)
+ args = parse_symbol_arg
+ rw = "?"
+
+ # If nodoc is given, don't document any of them
+
+ tmp = RDoc::CodeObject.new
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
+
+ regexp = /^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i
+ if regexp =~ comment.text then
+ comment.text = comment.text.sub(regexp, '')
+ rw = case $1
+ when 'attr_reader' then 'R'
+ when 'attr_writer' then 'W'
+ else 'RW'
+ end
+ name = $3 unless $3.empty?
+ end
+
+ if name then
+ att = create_attr context, single, name, rw, comment
+ else
+ args.each do |attr_name|
+ att = create_attr context, single, attr_name, rw, comment
+ end
+ end
+
+ att
+ end
+
+ ##
+ # Parses a meta-programmed method
+
+ def parse_meta_method(container, single, tk, comment)
+ column = tk[:char_no]
+ line_no = tk[:line_no]
+
+ start_collecting_tokens
+ add_token tk
+ add_token_listener self
+
+ skip_tkspace false
+
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
+ singleton = !!$~
+
+ name = parse_meta_method_name comment, tk
+
+ return unless name
+
+ meth = RDoc::MetaMethod.new get_tkread, name
+ record_location meth
+ meth.line = line_no
+ meth.singleton = singleton
+
+ remove_token_listener self
+
+ meth.start_collecting_tokens
+ indent = { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => ' ' * column }
+ position_comment = { :line_no => line_no, :char_no => 1, :kind => :on_comment }
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" }
+ meth.add_tokens [position_comment, newline, indent]
+ meth.add_tokens @token_stream
+
+ parse_meta_method_params container, single, meth, tk, comment
+
+ meth.comment = comment
+
+ @stats.add_method meth
+
+ meth
+ end
+
+ ##
+ # Parses the name of a metaprogrammed method. +comment+ is used to
+ # determine the name while +tk+ is used in an error message if the name
+ # cannot be determined.
+
+ def parse_meta_method_name comment, tk # :nodoc:
+ if comment.text.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
+ return $1 unless $1.empty?
+ end
+
+ name_t = get_tk
+
+ if :on_symbol == name_t[:kind] then
+ name_t[:text][1..-1]
+ elsif :on_tstring == name_t[:kind] then
+ name_t[:text][1..-2]
+ elsif :on_op == name_t[:kind] && '=' == name_t[:text] then # ignore
+ remove_token_listener self
+
+ nil
+ else
+ warn "unknown name token #{name_t.inspect} for meta-method '#{tk[:text]}'"
+ 'unknown'
+ end
+ end
+
+ ##
+ # Parses the parameters and block for a meta-programmed method.
+
+ def parse_meta_method_params container, single, meth, tk, comment # :nodoc:
+ token_listener meth do
+ meth.params = ''
+
+ look_for_directives_in meth, comment
+ comment.normalize
+ comment.extract_call_seq meth
+
+ container.add_method meth
+
+ last_tk = tk
+
+ while tk = get_tk do
+ if :on_semicolon == tk[:kind] then
+ break
+ elsif :on_nl == tk[:kind] then
+ break unless last_tk and :on_comma == last_tk[:kind]
+ elsif :on_sp == tk[:kind] then
+ # expression continues
+ elsif :on_kw == tk[:kind] && 'do' == tk[:text] then
+ parse_statements container, single, meth
+ break
+ else
+ last_tk = tk
+ end
+ end
+ end
+ end
+
+ ##
+ # Parses a normal method defined by +def+
+
+ def parse_method(container, single, tk, comment)
+ singleton = nil
+ added_container = false
+ name = nil
+ column = tk[:char_no]
+ line_no = tk[:line_no]
+
+ start_collecting_tokens
+ add_token tk
+
+ token_listener self do
+ prev_container = container
+ name, container, singleton = parse_method_name container
+ added_container = container != prev_container
+ end
+
+ return unless name
+
+ meth = RDoc::AnyMethod.new get_tkread, name
+ look_for_directives_in meth, comment
+ meth.singleton = single == SINGLE ? true : singleton
+
+ record_location meth
+ meth.line = line_no
+
+ meth.start_collecting_tokens
+ indent = { :line_no => 1, :char_no => 1, :kind => :on_sp, :text => ' ' * column }
+ token = { :line_no => line_no, :char_no => 1, :kind => :on_comment }
+ token[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
+ newline = { :line_no => 0, :char_no => 0, :kind => :on_nl, :text => "\n" }
+ meth.add_tokens [token, newline, indent]
+ meth.add_tokens @token_stream
+
+ parse_method_params_and_body container, single, meth, added_container
+
+ comment.normalize
+ comment.extract_call_seq meth
+
+ meth.comment = comment
+
+ # after end modifiers
+ read_documentation_modifiers meth, RDoc::METHOD_MODIFIERS
+
+ @stats.add_method meth
+ end
+
+ ##
+ # Parses the parameters and body of +meth+
+
+ def parse_method_params_and_body container, single, meth, added_container
+ token_listener meth do
+ parse_method_parameters meth
+
+ if meth.document_self or not @track_visibility then
+ container.add_method meth
+ elsif added_container then
+ container.document_self = false
+ end
+
+ # Having now read the method parameters and documentation modifiers, we
+ # now know whether we have to rename #initialize to ::new
+
+ if meth.name == "initialize" && !meth.singleton then
+ if meth.dont_rename_initialize then
+ meth.visibility = :protected
+ else
+ meth.singleton = true
+ meth.name = "new"
+ meth.visibility = :public
+ end
+ end
+
+ parse_statements container, single, meth
+ end
+ end
+
+ ##
+ # Parses a method that needs to be ignored.
+
+ def parse_method_dummy container
+ dummy = RDoc::Context.new
+ dummy.parent = container
+ dummy.store = container.store
+ skip_method dummy
+ end
+
+ ##
+ # Parses the name of a method in +container+.
+ #
+ # Returns the method name, the container it is in (for def Foo.name) and if
+ # it is a singleton or regular method.
+
+ def parse_method_name container # :nodoc:
+ skip_tkspace
+ name_t = get_tk
+ back_tk = skip_tkspace(false)
+ singleton = false
+
+ dot = get_tk
+ if dot[:kind] == :on_period || (dot[:kind] == :on_op && dot[:text] == '::') then
+ singleton = true
+
+ name, container = parse_method_name_singleton container, name_t
+ else
+ unget_tk dot
+ back_tk.reverse_each do |token|
+ unget_tk token
+ end
+
+ name = parse_method_name_regular container, name_t
+ end
+
+ return name, container, singleton
+ end
+
+ ##
+ # For the given +container+ and initial name token +name_t+ the method name
+ # is parsed from the token stream for a regular method.
+
+ def parse_method_name_regular container, name_t # :nodoc:
+ if :on_op == name_t[:kind] && (%w{* & [] []= <<}.include?(name_t[:text])) then
+ name_t[:text]
+ else
+ unless [:on_kw, :on_const, :on_ident].include?(name_t[:kind]) then
+ warn "expected method name token, . or ::, got #{name_t.inspect}"
+ skip_method container
+ return
+ end
+ name_t[:text]
+ end
+ end
+
+ ##
+ # For the given +container+ and initial name token +name_t+ the method name
+ # and the new +container+ (if necessary) are parsed from the token stream
+ # for a singleton method.
+
+ def parse_method_name_singleton container, name_t # :nodoc:
+ skip_tkspace
+ name_t2 = get_tk
+
+ if (:on_kw == name_t[:kind] && 'self' == name_t[:text]) || (:on_op == name_t[:kind] && '%' == name_t[:text]) then
+ # NOTE: work around '[' being consumed early
+ if :on_lbracket == name_t2[:kind]
+ get_tk
+ name = '[]'
+ else
+ name = name_t2[:text]
+ end
+ elsif :on_const == name_t[:kind] then
+ name = name_t2[:text]
+
+ container = get_method_container container, name_t
+
+ return unless container
+
+ name
+ elsif :on_ident == name_t[:kind] || :on_ivar == name_t[:kind] || :on_gvar == name_t[:kind] then
+ parse_method_dummy container
+
+ name = nil
+ elsif (:on_kw == name_t[:kind]) && ('true' == name_t[:text] || 'false' == name_t[:text] || 'nil' == name_t[:text]) then
+ klass_name = "#{name_t[:text].capitalize}Class"
+ container = @store.find_class_named klass_name
+ container ||= @top_level.add_class RDoc::NormalClass, klass_name
+
+ name = name_t2[:text]
+ else
+ warn "unexpected method name token #{name_t.inspect}"
+ # break
+ skip_method container
+
+ name = nil
+ end
+
+ return name, container
+ end
+
+ ##
+ # Extracts +yield+ parameters from +method+
+
+ def parse_method_or_yield_parameters(method = nil,
+ modifiers = RDoc::METHOD_MODIFIERS)
+ skip_tkspace false
+ tk = get_tk
+ end_token = get_end_token tk
+ return '' unless end_token
+
+ nest = 0
+ continue = false
+
+ while tk != nil do
+ case tk[:kind]
+ when :on_semicolon then
+ break if nest == 0
+ when :on_lbrace then
+ nest += 1
+ when :on_rbrace then
+ nest -= 1
+ if nest <= 0
+ # we might have a.each { |i| yield i }
+ unget_tk(tk) if nest < 0
+ break
+ end
+ when :on_lparen then
+ nest += 1
+ when end_token[:kind] then
+ if end_token[:kind] == :on_rparen
+ nest -= 1
+ break if nest <= 0
+ else
+ break
+ end
+ when :on_rparen then
+ nest -= 1
+ when :on_comment, :on_embdoc then
+ @read.pop
+ if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
+ (!continue or (tk[:state] & RDoc::RipperStateLex::EXPR_LABEL) != 0) then
+ if method && method.block_params.nil? then
+ unget_tk tk
+ read_documentation_modifiers method, modifiers
+ end
+ break if !continue and nest <= 0
+ end
+ when :on_comma then
+ continue = true
+ when :on_ident then
+ continue = false if continue
+ end
+ tk = get_tk
+ end
+
+ get_tkread_clean(/\s+/, ' ')
+ end
+
+ ##
+ # Capture the method's parameters. Along the way, look for a comment
+ # containing:
+ #
+ # # yields: ....
+ #
+ # and add this as the block_params for the method
+
+ def parse_method_parameters method
+ res = parse_method_or_yield_parameters method
+
+ res = "(#{res})" unless res =~ /\A\(/
+ method.params = res unless method.params
+
+ return if method.block_params
+
+ skip_tkspace false
+ read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
+ end
+
+ ##
+ # Parses an RDoc::NormalModule in +container+ with +comment+
+
+ def parse_module container, single, tk, comment
+ container, name_t, = get_class_or_module container
+
+ name = name_t[:text]
+
+ mod = container.add_module RDoc::NormalModule, name
+ mod.ignore unless container.document_children
+ record_location mod
+
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+ mod.add_comment comment, @top_level
+ parse_statements mod
+
+ # after end modifiers
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+
+ @stats.add_module mod
+ end
+
+ ##
+ # Parses an RDoc::Require in +context+ containing +comment+
+
+ def parse_require(context, comment)
+ skip_tkspace_comment
+ tk = get_tk
+
+ if :on_lparen == tk[:kind] then
+ skip_tkspace_comment
+ tk = get_tk
+ end
+
+ name = tk[:text][1..-2] if :on_tstring == tk[:kind]
+
+ if name then
+ @top_level.add_require RDoc::Require.new(name, comment)
+ else
+ unget_tk tk
+ end
+ end
+
+ ##
+ # Parses a rescue
+
+ def parse_rescue
+ skip_tkspace false
+
+ while tk = get_tk
+ case tk[:kind]
+ when :on_nl, :on_semicolon, :on_comment then
+ break
+ when :on_comma then
+ skip_tkspace false
+
+ get_tk if :on_nl == peek_tk[:kind]
+ end
+
+ skip_tkspace false
+ end
+ end
+
+ ##
+ # The core of the Ruby parser.
+
+ def parse_statements(container, single = NORMAL, current_method = nil,
+ comment = new_comment(''))
+ raise 'no' unless RDoc::Comment === comment
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
+
+ nest = 1
+ save_visibility = container.visibility
+
+ non_comment_seen = true
+
+ while tk = get_tk do
+ keep_comment = false
+ try_parse_comment = false
+
+ non_comment_seen = true unless (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
+
+ case tk[:kind]
+ when :on_nl, :on_ignored_nl, :on_comment, :on_embdoc then
+ if :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
+ skip_tkspace
+ tk = get_tk
+ else
+ past_tokens = @read.size > 1 ? @read[0..-2] : []
+ nl_position = 0
+ past_tokens.reverse.each_with_index do |read_tk, i|
+ if read_tk =~ /^\n$/ then
+ nl_position = (past_tokens.size - 1) - i
+ break
+ elsif read_tk =~ /^#.*\n$/ then
+ nl_position = ((past_tokens.size - 1) - i) + 1
+ break
+ end
+ end
+ comment_only_line = past_tokens[nl_position..-1].all?{ |c| c =~ /^\s+$/ }
+ unless comment_only_line then
+ tk = get_tk
+ end
+ end
+
+ if tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
+ if non_comment_seen then
+ # Look for RDoc in a comment about to be thrown away
+ non_comment_seen = parse_comment container, tk, comment unless
+ comment.empty?
+
+ comment = ''
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
+ end
+
+ while tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) do
+ comment += tk[:text]
+ comment += "\n" unless "\n" == tk[:text].chars.to_a.last
+
+ if tk[:text].size > 1 && "\n" == tk[:text].chars.to_a.last then
+ skip_tkspace false # leading spaces
+ end
+ tk = get_tk
+ end
+
+ comment = new_comment comment
+
+ unless comment.empty? then
+ look_for_directives_in container, comment
+
+ if container.done_documenting then
+ throw :eof if RDoc::TopLevel === container
+ container.ongoing_visibility = save_visibility
+ end
+ end
+
+ keep_comment = true
+ else
+ non_comment_seen = true
+ end
+
+ unget_tk tk
+ keep_comment = true
+ container.current_line_visibility = nil
+
+ when :on_kw then
+ case tk[:text]
+ when 'class' then
+ parse_class container, single, tk, comment
+
+ when 'module' then
+ parse_module container, single, tk, comment
+
+ when 'def' then
+ parse_method container, single, tk, comment
+
+ when 'alias' then
+ parse_alias container, single, tk, comment unless current_method
+
+ when 'yield' then
+ if current_method.nil? then
+ warn "Warning: yield outside of method" if container.document_self
+ else
+ parse_yield container, single, tk, current_method
+ end
+
+ when 'until', 'while' then
+ if (tk[:state] & RDoc::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ skip_optional_do_after_expression
+ end
+
+ # Until and While can have a 'do', which shouldn't increase the nesting.
+ # We can't solve the general case, but we can handle most occurrences by
+ # ignoring a do at the end of a line.
+
+ # 'for' is trickier
+ when 'for' then
+ nest += 1
+ skip_for_variable
+ skip_optional_do_after_expression
+
+ when 'case', 'do', 'if', 'unless', 'begin' then
+ if (tk[:state] & RDoc::RipperStateLex::EXPR_LABEL) == 0
+ nest += 1
+ end
+
+ when 'super' then
+ current_method.calls_super = true if current_method
+
+ when 'rescue' then
+ parse_rescue
+
+ when 'end' then
+ nest -= 1
+ if nest == 0 then
+ container.ongoing_visibility = save_visibility
+
+ parse_comment container, tk, comment unless comment.empty?
+
+ return
+ end
+ end
+
+ when :on_const then
+ unless parse_constant container, tk, comment, current_method then
+ try_parse_comment = true
+ end
+
+ when :on_ident then
+ if nest == 1 and current_method.nil? then
+ keep_comment = parse_identifier container, single, tk, comment
+ end
+
+ case tk[:text]
+ when "require" then
+ parse_require container, comment
+ when "include" then
+ parse_extend_or_include RDoc::Include, container, comment
+ when "extend" then
+ parse_extend_or_include RDoc::Extend, container, comment
+ end
+
+ else
+ try_parse_comment = nest == 1
+ end
+
+ if try_parse_comment then
+ non_comment_seen = parse_comment container, tk, comment unless
+ comment.empty?
+
+ keep_comment = false
+ end
+
+ unless keep_comment then
+ comment = new_comment ''
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
+ container.params = nil
+ container.block_params = nil
+ end
+
+ consume_trailing_spaces
+ end
+
+ container.params = nil
+ container.block_params = nil
+ end
+
+ ##
+ # Parse up to +no+ symbol arguments
+
+ def parse_symbol_arg(no = nil)
+ skip_tkspace_comment
+
+ tk = get_tk
+ if tk[:kind] == :on_lparen
+ parse_symbol_arg_paren no
+ else
+ parse_symbol_arg_space no, tk
+ end
+ end
+
+ ##
+ # Parses up to +no+ symbol arguments surrounded by () and places them in
+ # +args+.
+
+ def parse_symbol_arg_paren no # :nodoc:
+ args = []
+
+ loop do
+ skip_tkspace_comment
+ if tk1 = parse_symbol_in_arg
+ args.push tk1
+ break if no and args.size >= no
+ end
+
+ skip_tkspace_comment
+ case (tk2 = get_tk)[:kind]
+ when :on_rparen
+ break
+ when :on_comma
+ else
+ warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
+ break
+ end
+ end
+
+ args
+ end
+
+ ##
+ # Parses up to +no+ symbol arguments separated by spaces and places them in
+ # +args+.
+
+ def parse_symbol_arg_space no, tk # :nodoc:
+ args = []
+
+ unget_tk tk
+ if tk = parse_symbol_in_arg
+ args.push tk
+ return args if no and args.size >= no
+ end
+
+ loop do
+ skip_tkspace false
+
+ tk1 = get_tk
+ if tk1.nil? || :on_comma != tk1[:kind] then
+ unget_tk tk1
+ break
+ end
+
+ skip_tkspace_comment
+ if tk = parse_symbol_in_arg
+ args.push tk
+ break if no and args.size >= no
+ end
+ end
+
+ args
+ end
+
+ ##
+ # Returns symbol text from the next token
+
+ def parse_symbol_in_arg
+ tk = get_tk
+ if :on_symbol == tk[:kind] then
+ tk[:text].sub(/^:/, '')
+ elsif :on_tstring == tk[:kind] then
+ tk[:text][1..-2]
+ elsif :on_dstring == tk[:kind] or :on_ident == tk[:kind] then
+ nil # ignore
+ else
+ warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
+ nil
+ end
+ end
+
+ ##
+ # Parses statements in the top-level +container+
+
+ def parse_top_level_statements container
+ comment = collect_first_comment
+
+ look_for_directives_in container, comment
+
+ throw :eof if container.done_documenting
+
+ @markup = comment.format
+
+ # HACK move if to RDoc::Context#comment=
+ container.comment = comment if container.document_self unless comment.empty?
+
+ parse_statements container, NORMAL, nil, comment
+ end
+
+ ##
+ # Determines the visibility in +container+ from +tk+
+
+ def parse_visibility(container, single, tk)
+ vis_type, vis, singleton = get_visibility_information tk, single
+
+ skip_tkspace_comment false
+
+ ptk = peek_tk
+ # Ryan Davis suggested the extension to ignore modifiers, because he
+ # often writes
+ #
+ # protected unless $TESTING
+ #
+ if [:on_nl, :on_semicolon].include?(ptk[:kind]) || (:on_kw == ptk[:kind] && (['if', 'unless'].include?(ptk[:text]))) then
+ container.ongoing_visibility = vis
+ elsif :on_kw == ptk[:kind] && 'def' == ptk[:text]
+ container.current_line_visibility = vis
+ else
+ update_visibility container, vis_type, vis, singleton
+ end
+ end
+
+ ##
+ # Parses a Module#private_constant or Module#public_constant call from +tk+.
+
+ def parse_constant_visibility(container, single, tk)
+ args = parse_symbol_arg
+ case tk[:text]
+ when 'private_constant'
+ vis = :private
+ when 'public_constant'
+ vis = :public
+ else
+ raise RDoc::Error, 'Unreachable'
+ end
+ container.set_constant_visibility_for args, vis
+ end
+
+ ##
+ # Determines the block parameter for +context+
+
+ def parse_yield(context, single, tk, method)
+ return if method.block_params
+
+ get_tkread
+ method.block_params = parse_method_or_yield_parameters
+ end
+
+ ##
+ # Directives are modifier comments that can appear after class, module, or
+ # method names. For example:
+ #
+ # def fred # :yields: a, b
+ #
+ # or:
+ #
+ # class MyClass # :nodoc:
+ #
+ # We return the directive name and any parameters as a two element array if
+ # the name is in +allowed+. A directive can be found anywhere up to the end
+ # of the current line.
+
+ def read_directive allowed
+ tokens = []
+
+ while tk = get_tk do
+ tokens << tk
+
+ if :on_nl == tk[:kind] or (:on_kw == tk[:kind] && 'def' == tk[:text]) then
+ return
+ elsif :on_comment == tk[:kind] or :on_embdoc == tk[:kind] then
+ return unless tk[:text] =~ /\s*:?([\w-]+):\s*(.*)/
+
+ directive = $1.downcase
+
+ return [directive, $2] if allowed.include? directive
+
+ return
+ end
+ end
+ ensure
+ unless tokens.length == 1 and (:on_comment == tokens.first[:kind] or :on_embdoc == tokens.first[:kind]) then
+ tokens.reverse_each do |token|
+ unget_tk token
+ end
+ end
+ end
+
+ ##
+ # Handles directives following the definition for +context+ (any
+ # RDoc::CodeObject) if the directives are +allowed+ at this point.
+ #
+ # See also RDoc::Markup::PreProcess#handle_directive
+
+ def read_documentation_modifiers context, allowed
+ skip_tkspace(false)
+ directive, value = read_directive allowed
+
+ return unless directive
+
+ @preprocess.handle_directive '', directive, value, context do |dir, param|
+ if %w[notnew not_new not-new].include? dir then
+ context.dont_rename_initialize = true
+
+ true
+ end
+ end
+ end
+
+ ##
+ # Records the location of this +container+ in the file for this parser and
+ # adds it to the list of classes and modules in the file.
+
+ def record_location container # :nodoc:
+ case container
+ when RDoc::ClassModule then
+ @top_level.add_to_classes_or_modules container
+ end
+
+ container.record_location @top_level
+ end
+
+ ##
+ # Scans this Ruby file for Ruby constructs
+
+ def scan
+ reset
+
+ catch :eof do
+ begin
+ parse_top_level_statements @top_level
+
+ rescue StandardError => e
+ if @content.include?('<%') and @content.include?('%>') then
+ # Maybe, this is ERB.
+ $stderr.puts "\033[2KRDoc detects ERB file. Skips it for compatibility:"
+ $stderr.puts @file_name
+ return
+ end
+ bytes = ''
+
+ if @scanner_point >= @scanner.size
+ now_line_no = @scanner[@scanner.size - 1][:line_no]
+ else
+ now_line_no = peek_tk[:line_no]
+ end
+
+ $stderr.puts <<-EOF
+
+#{self.class} failure around line #{now_line_no} of
+#{@file_name}
+
+ EOF
+
+ unless bytes.empty? then
+ $stderr.puts
+ now_line_no = peek_tk[:line_no]
+ start_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no }
+ end_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no + 1 } - 1
+ $stderr.puts @scanner[start_index..end_index].join
+ end
+
+ raise e
+ end
+ end
+
+ @top_level
+ end
+
+ ##
+ # while, until, and for have an optional do
+
+ def skip_optional_do_after_expression
+ skip_tkspace false
+ tk = get_tk
+
+ b_nest = 0
+ nest = 0
+
+ loop do
+ break unless tk
+ case tk[:kind]
+ when :on_semicolon, :on_nl, :on_ignored_nl then
+ break if b_nest.zero?
+ when :on_lparen then
+ nest += 1
+ when :on_rparen then
+ nest -= 1
+ when :on_kw then
+ case tk[:text]
+ when 'begin'
+ b_nest += 1
+ when 'end'
+ b_nest -= 1
+ when 'do'
+ break if nest.zero?
+ end
+ when :on_comment, :on_embdoc then
+ if b_nest.zero? and "\n" == tk[:text][-1] then
+ break
+ end
+ end
+ tk = get_tk
+ end
+
+ skip_tkspace false
+
+ get_tk if peek_tk && :on_kw == peek_tk[:kind] && 'do' == peek_tk[:text]
+ end
+
+ ##
+ # skip the var [in] part of a 'for' statement
+
+ def skip_for_variable
+ skip_tkspace false
+ get_tk
+ skip_tkspace false
+ tk = get_tk
+ unget_tk(tk) unless :on_kw == tk[:kind] and 'in' == tk[:text]
+ end
+
+ ##
+ # Skips the next method in +container+
+
+ def skip_method container
+ meth = RDoc::AnyMethod.new "", "anon"
+ parse_method_parameters meth
+ parse_statements container, false, meth
+ end
+
+ ##
+ # Skip spaces until a comment is found
+
+ def skip_tkspace_comment(skip_nl = true)
+ loop do
+ skip_tkspace skip_nl
+ next_tk = peek_tk
+ return if next_tk.nil? || (:on_comment != next_tk[:kind] and :on_embdoc != next_tk[:kind])
+ get_tk
+ end
+ end
+
+ ##
+ # Updates visibility in +container+ from +vis_type+ and +vis+.
+
+ def update_visibility container, vis_type, vis, singleton # :nodoc:
+ new_methods = []
+
+ case vis_type
+ when 'module_function' then
+ args = parse_symbol_arg
+ container.set_visibility_for args, :private, false
+
+ container.methods_matching args do |m|
+ s_m = m.dup
+ record_location s_m
+ s_m.singleton = true
+ new_methods << s_m
+ end
+ when 'public_class_method', 'private_class_method' then
+ args = parse_symbol_arg
+
+ container.methods_matching args, true do |m|
+ if m.parent != container then
+ m = m.dup
+ record_location m
+ new_methods << m
+ end
+
+ m.visibility = vis
+ end
+ else
+ args = parse_symbol_arg
+ container.set_visibility_for args, vis, singleton
+ end
+
+ new_methods.each do |method|
+ case method
+ when RDoc::AnyMethod then
+ container.add_method method
+ when RDoc::Attr then
+ container.add_attribute method
+ end
+ method.visibility = vis
+ end
+ end
+
+ ##
+ # Prints +message+ to +$stderr+ unless we're being quiet
+
+ def warn message
+ @options.warn make_message message
+ end
+
+end
diff --git a/lib/rdoc/parser/ruby_tools.rb b/lib/rdoc/parser/ruby_tools.rb
new file mode 100644
index 0000000000..1f621cd32e
--- /dev/null
+++ b/lib/rdoc/parser/ruby_tools.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+##
+# Collection of methods for writing parsers
+
+module RDoc::Parser::RubyTools
+
+ ##
+ # Adds a token listener +obj+, but you should probably use token_listener
+
+ def add_token_listener(obj)
+ @token_listeners ||= []
+ @token_listeners << obj
+ end
+
+ ##
+ # Fetches the next token from the scanner
+
+ def get_tk
+ tk = nil
+
+ if @tokens.empty? then
+ if @scanner_point >= @scanner.size
+ return nil
+ else
+ tk = @scanner[@scanner_point]
+ @scanner_point += 1
+ @read.push tk[:text]
+ puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
+ end
+ else
+ @read.push @unget_read.shift
+ tk = @tokens.shift
+ puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
+ end
+
+ if tk == nil || :on___end__ == tk[:kind]
+ tk = nil
+ end
+
+ return nil unless tk
+
+ # inform any listeners of our shiny new token
+ @token_listeners.each do |obj|
+ obj.add_token(tk)
+ end if @token_listeners
+
+ tk
+ end
+
+ ##
+ # Reads and returns all tokens up to one of +tokens+. Leaves the matched
+ # token in the token list.
+
+ def get_tk_until(*tokens)
+ read = []
+
+ loop do
+ tk = get_tk
+
+ case tk
+ when *tokens then
+ unget_tk tk
+ break
+ end
+
+ read << tk
+ end
+
+ read
+ end
+
+ ##
+ # Retrieves a String representation of the read tokens
+
+ def get_tkread
+ read = @read.join("")
+ @read = []
+ read
+ end
+
+ ##
+ # Peek equivalent for get_tkread
+
+ def peek_read
+ @read.join('')
+ end
+
+ ##
+ # Peek at the next token, but don't remove it from the stream
+
+ def peek_tk
+ unget_tk(tk = get_tk)
+ tk
+ end
+
+ ##
+ # Removes the token listener +obj+
+
+ def remove_token_listener(obj)
+ @token_listeners.delete(obj)
+ end
+
+ ##
+ # Resets the tools
+
+ def reset
+ @read = []
+ @tokens = []
+ @unget_read = []
+ @nest = 0
+ @scanner_point = 0
+ end
+
+ def tk_nl?(tk)
+ :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
+ end
+
+ ##
+ # Skips whitespace tokens including newlines if +skip_nl+ is true
+
+ def skip_tkspace(skip_nl = true)
+ tokens = []
+
+ while (tk = get_tk) and (:on_sp == tk[:kind] or (skip_nl and tk_nl?(tk))) do
+ tokens.push(tk)
+ end
+
+ unget_tk(tk)
+ tokens
+ end
+
+ ##
+ # Has +obj+ listen to tokens
+
+ def token_listener(obj)
+ add_token_listener obj
+ yield
+ ensure
+ remove_token_listener obj
+ end
+
+ ##
+ # Returns +tk+ to the scanner
+
+ def unget_tk(tk)
+ @tokens.unshift tk
+ @unget_read.unshift @read.pop
+
+ # Remove this token from any listeners
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
+
+ nil
+ end
+
+end
+
+
diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb
new file mode 100644
index 0000000000..b1dabad0f8
--- /dev/null
+++ b/lib/rdoc/parser/simple.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+##
+# Parse a non-source file. We basically take the whole thing as one big
+# comment.
+
+class RDoc::Parser::Simple < RDoc::Parser
+
+ include RDoc::Parser::Text
+
+ parse_files_matching(//)
+
+ attr_reader :content # :nodoc:
+
+ ##
+ # Prepare to parse a plain file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+
+ @content = preprocess.handle @content, @top_level
+ end
+
+ ##
+ # Extract the file contents and attach them to the TopLevel as a comment
+
+ def scan
+ comment = remove_coding_comment @content
+ comment = remove_private_comment comment
+
+ comment = RDoc::Comment.new comment, @top_level
+
+ @top_level.comment = comment
+ @top_level
+ end
+
+ ##
+ # Removes the encoding magic comment from +text+
+
+ def remove_coding_comment text
+ text.sub(/\A# .*coding[=:].*$/, '')
+ end
+
+ ##
+ # Removes private comments.
+ #
+ # Unlike RDoc::Comment#remove_private this implementation only looks for two
+ # dashes at the beginning of the line. Three or more dashes are considered
+ # to be a rule and ignored.
+
+ def remove_private_comment comment
+ # Workaround for gsub encoding for Ruby 1.9.2 and earlier
+ empty = ''
+ empty = RDoc::Encoding.change_encoding empty, comment.encoding
+
+ comment = comment.gsub(%r%^--\n.*?^\+\+\n?%m, empty)
+ comment.sub(%r%^--\n.*%m, empty)
+ end
+
+end
diff --git a/lib/rdoc/parser/text.rb b/lib/rdoc/parser/text.rb
new file mode 100644
index 0000000000..01de0cc595
--- /dev/null
+++ b/lib/rdoc/parser/text.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+##
+# Indicates this parser is text and doesn't contain code constructs.
+#
+# Include this module in a RDoc::Parser subclass to make it show up as a file,
+# not as part of a class or module.
+#--
+# This is not named File to avoid overriding ::File
+
+module RDoc::Parser::Text
+end
+
diff --git a/lib/rdoc/parsers/parse_c.rb b/lib/rdoc/parsers/parse_c.rb
deleted file mode 100644
index fdec9c6b23..0000000000
--- a/lib/rdoc/parsers/parse_c.rb
+++ /dev/null
@@ -1,697 +0,0 @@
- # We attempt to parse C extension files. Basically we look for
- # the standard patterns that you find in extensions: <tt>rb_define_class,
- # rb_define_method</tt> and so on. We also try to find the corresponding
- # C source for the methods and extract comments, but if we fail
- # we don't worry too much.
- #
- # The comments associated with a Ruby method are extracted from the C
- # comment block associated with the routine that _implements_ that
- # method, that is to say the method whose name is given in the
- # <tt>rb_define_method</tt> call. For example, you might write:
- #
- # /*
- # * Returns a new array that is a one-dimensional flattening of this
- # * array (recursively). That is, for every element that is an array,
- # * extract its elements into the new array.
- # *
- # * s = [ 1, 2, 3 ] #=> [1, 2, 3]
- # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
- # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
- # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- # */
- # static VALUE
- # rb_ary_flatten(ary)
- # VALUE ary;
- # {
- # ary = rb_obj_dup(ary);
- # rb_ary_flatten_bang(ary);
- # return ary;
- # }
- #
- # ...
- #
- # void
- # Init_Array()
- # {
- # ...
- # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
- #
- # Here RDoc will determine from the rb_define_method line that there's a
- # method called "flatten" in class Array, and will look for the implementation
- # in the method rb_ary_flatten. It will then use the comment from that
- # method in the HTML output. This method must be in the same source file
- # as the rb_define_method.
- #
- # C classes can be diagramed (see /tc/dl/ruby/ruby/error.c), and RDoc
- # integrates C and Ruby source into one tree
- #
- # The comment blocks may include special direcives:
- #
- # [Document-class: <i>name</i>]
- # This comment block is documentation for the given class. Use this
- # when the <tt>Init_xxx</tt> method is not named after the class.
- #
- # [Document-method: <i>name</i>]
- # This comment documents the named method. Use when RDoc cannot outomatically
- # find the method from it's declaration
- #
- # [call-seq: <i>text up to an empty line</i>]
- # Because C source doesn't give descripive names to Ruby-level parameters,
- # you need to document the calling sequence explicitly
- #
- # In additon, RDoc assumes by default that the C method implementing a
- # Ruby function is in the same source file as the rb_define_method call.
- # If this isn't the case, add the comment
- #
- # rb_define_method(....); // in: filename
- #
- # As an example, we might have an extension that defines multiple classes
- # in its Init_xxx method. We could document them using
- #
- #
- # /*
- # * Document-class: MyClass
- # *
- # * Encapsulate the writing and reading of the configuration
- # * file. ...
- # */
- #
- # /*
- # * Document-method: read_value
- # *
- # * call-seq:
- # * cfg.read_value(key) -> value
- # * cfg.read_value(key} { |key| } -> value
- # *
- # * Return the value corresponding to +key+ from the configuration.
- # * In the second form, if the key isn't found, invoke the
- # * block and return its value.
- # */
- #
-
-
- # Classes and modules built in to the interpreter. We need
- # these to define superclasses of user objects
-
-require "rdoc/code_objects"
-require "rdoc/parsers/parserfactory"
-
-
-module RDoc
-
- KNOWN_CLASSES = {
- "rb_cObject" => "Object",
- "rb_cArray" => "Array",
- "rb_cBignum" => "Bignum",
- "rb_cClass" => "Class",
- "rb_cDir" => "Dir",
- "rb_cData" => "Data",
- "rb_cFalseClass" => "FalseClass",
- "rb_cFile" => "File",
- "rb_cFixnum" => "Fixnum",
- "rb_cFloat" => "Float",
- "rb_cHash" => "Hash",
- "rb_cInteger" => "Integer",
- "rb_cIO" => "IO",
- "rb_cModule" => "Module",
- "rb_cNilClass" => "NilClass",
- "rb_cNumeric" => "Numeric",
- "rb_cProc" => "Proc",
- "rb_cRange" => "Range",
- "rb_cRegexp" => "Regexp",
- "rb_cString" => "String",
- "rb_cSymbol" => "Symbol",
- "rb_cThread" => "Thread",
- "rb_cTime" => "Time",
- "rb_cTrueClass" => "TrueClass",
- "rb_cStruct" => "Struct",
- "rb_eException" => "Exception",
- "rb_eStandardError" => "StandardError",
- "rb_eSystemExit" => "SystemExit",
- "rb_eInterrupt" => "Interrupt",
- "rb_eSignal" => "Signal",
- "rb_eFatal" => "Fatal",
- "rb_eArgError" => "ArgError",
- "rb_eEOFError" => "EOFError",
- "rb_eIndexError" => "IndexError",
- "rb_eRangeError" => "RangeError",
- "rb_eIOError" => "IOError",
- "rb_eRuntimeError" => "RuntimeError",
- "rb_eSecurityError" => "SecurityError",
- "rb_eSystemCallError" => "SystemCallError",
- "rb_eTypeError" => "TypeError",
- "rb_eZeroDivError" => "ZeroDivError",
- "rb_eNotImpError" => "NotImpError",
- "rb_eNoMemError" => "NoMemError",
- "rb_eFloatDomainError" => "FloatDomainError",
- "rb_eScriptError" => "ScriptError",
- "rb_eNameError" => "NameError",
- "rb_eSyntaxError" => "SyntaxError",
- "rb_eLoadError" => "LoadError",
-
- "rb_mKernel" => "Kernel",
- "rb_mComparable" => "Comparable",
- "rb_mEnumerable" => "Enumerable",
- "rb_mPrecision" => "Precision",
- "rb_mErrno" => "Errno",
- "rb_mFileTest" => "FileTest",
- "rb_mGC" => "GC",
- "rb_mMath" => "Math",
- "rb_mProcess" => "Process"
-
- }
-
- # See rdoc/c_parse.rb
-
- class C_Parser
-
-
- extend ParserFactory
- parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
-
- @@known_bodies = {}
-
- # prepare to parse a C file
- def initialize(top_level, file_name, body, options, stats)
- @known_classes = KNOWN_CLASSES.dup
- @body = handle_tab_width(handle_ifdefs_in(body))
- @options = options
- @stats = stats
- @top_level = top_level
- @classes = Hash.new
- @file_dir = File.dirname(file_name)
- @progress = $stderr unless options.quiet
- end
-
- # Extract the classes/modules and methods from a C file
- # and return the corresponding top-level object
- def scan
- remove_commented_out_lines
- do_classes
- do_constants
- do_methods
- do_includes
- do_aliases
- @top_level
- end
-
- #######
- private
- #######
-
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
- end
- end
-
- def warn(msg)
- $stderr.puts
- $stderr.puts msg
- $stderr.flush
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '')
- comment.sub!(/\/?\*--.*/m, '')
- end
-
- # remove lines that are commented out that might otherwise get
- # picked up when scanning for classes and methods
-
- def remove_commented_out_lines
- @body.gsub!(%r{//.*rb_define_}, '//')
- end
-
- def handle_class_module(var_name, class_mod, class_name, parent, in_module)
- progress(class_mod[0, 1])
-
- parent_name = @known_classes[parent] || parent
-
- if in_module
- enclosure = @classes[in_module]
- unless enclosure
- if enclosure = @known_classes[in_module]
- handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
- enclosure, nil, nil)
- enclosure = @classes[in_module]
- end
- end
- unless enclosure
- warn("Enclosing class/module '#{in_module}' for " +
- "#{class_mod} #{class_name} not known")
- return
- end
- else
- enclosure = @top_level
- end
-
- if class_mod == "class"
- cm = enclosure.add_class(NormalClass, class_name, parent_name)
- @stats.num_classes += 1
- else
- cm = enclosure.add_module(NormalModule, class_name)
- @stats.num_modules += 1
- end
- cm.record_location(enclosure.toplevel)
-
- find_class_comment(cm.full_name, cm)
- @classes[var_name] = cm
- @known_classes[var_name] = cm.full_name
- end
-
-
- ############################################################
-
- def find_class_comment(class_name, class_meth)
- comment = nil
- if @body =~ %r{((?>/\*.*?\*/\s+))
- (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)}xmi
- comment = $1
- elsif @body =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
- comment = $2
- end
- class_meth.comment = mangle_comment(comment) if comment
- end
-
- ############################################################
-
- def do_classes
- @body.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
- |var_name, class_name|
- handle_class_module(var_name, "module", class_name, nil, nil)
- end
-
- # The '.' lets us handle SWIG-generated files
- @body.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
- \(
- \s*"(\w+)",
- \s*(\w+)\s*
- \)/mx) do
-
- |var_name, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @body.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
- |var_name, class_name, parent|
- parent = nil if parent == "0"
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @body.scan(/(\w+)\s* = \s*rb_define_module_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)"
- \s*\)/mx) do
-
- |var_name, in_module, class_name|
- handle_class_module(var_name, "module", class_name, nil, in_module)
- end
-
- @body.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)",
- \s*(\w+)\s*
- \s*\)/mx) do
-
- |var_name, in_module, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, in_module)
- end
-
- end
-
- ###########################################################
-
- def do_constants
- @body.scan(%r{\Wrb_define_
- (
- variable |
- readonly_variable |
- const |
- global_const |
- )
- \s*\(
- (?:\s*(\w+),)?
- \s*"(\w+)",
- \s*(.*?)\s*\)\s*;
- }xm) do
-
- |type, var_name, const_name, definition|
- var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
- handle_constants(type, var_name, const_name, definition)
- end
- end
-
- ############################################################
-
- def do_methods
-
- @body.scan(%r{rb_define_
- (
- singleton_method |
- method |
- module_function |
- private_method
- )
- \s*\(\s*([\w\.]+),
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do
- |type, var_name, meth_name, meth_body, param_count, source_file|
- #"
-
- # Ignore top-object and weird struct.c dynamic stuff
- next if var_name == "ruby_top_self"
- next if var_name == "nstr"
- next if var_name == "envtbl"
- next if var_name == "argf" # it'd be nice to handle this one
-
- var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_method(type, var_name, meth_name,
- meth_body, param_count, source_file)
- end
-
- @body.scan(%r{rb_define_attr\(
- \s*([\w\.]+),
- \s*"([^"]+)",
- \s*(\d+),
- \s*(\d+)\s*\);
- }xm) do #"
- |var_name, attr_name, attr_reader, attr_writer|
-
- #var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_attr(var_name, attr_name,
- attr_reader.to_i != 0,
- attr_writer.to_i != 0)
- end
-
- @body.scan(%r{rb_define_global_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do #"
- |meth_name, meth_body, param_count, source_file|
- handle_method("method", "rb_mKernel", meth_name,
- meth_body, param_count, source_file)
- end
-
- @body.scan(/define_filetest_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)/xm) do #"
- |meth_name, meth_body, param_count|
-
- handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
- handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
- end
- end
-
- ############################################################
-
- def do_aliases
- @body.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
- |var_name, new_name, old_name|
- @stats.num_methods += 1
- class_name = @known_classes[var_name] || var_name
- class_obj = find_class(var_name, class_name)
-
- class_obj.add_alias(Alias.new("", old_name, new_name, ""))
- end
- end
-
- ############################################################
-
- def handle_constants(type, var_name, const_name, definition)
- #@stats.num_constants += 1
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- unless class_obj
- warn("Enclosing class/module '#{const_name}' for not known")
- return
- end
-
- comment = find_const_comment(type, const_name)
-
- con = Constant.new(const_name, definition, mangle_comment(comment))
- class_obj.add_constant(con)
- end
-
- ###########################################################
-
- def find_const_comment(type, const_name)
- if @body =~ %r{((?>/\*.*?\*/\s+))
- rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ###########################################################
-
- def handle_attr(var_name, attr_name, reader, writer)
- rw = ''
- if reader
- #@stats.num_methods += 1
- rw << 'R'
- end
- if writer
- #@stats.num_methods += 1
- rw << 'W'
- end
-
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- if class_obj
- comment = find_attr_comment(attr_name)
- unless comment.empty?
- comment = mangle_comment(comment)
- end
- att = Attr.new('', attr_name, rw, comment)
- class_obj.add_attribute(att)
- end
-
- end
-
- ###########################################################
-
- def find_attr_comment(attr_name)
- if @body =~ %r{((?>/\*.*?\*/\s+))
- rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @body =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ###########################################################
-
- def handle_method(type, var_name, meth_name,
- meth_body, param_count, source_file = nil)
- progress(".")
-
- @stats.num_methods += 1
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- if class_obj
- if meth_name == "initialize"
- meth_name = "new"
- type = "singleton_method"
- end
- meth_obj = AnyMethod.new("", meth_name)
- meth_obj.singleton =
- %w{singleton_method module_function}.include?(type)
-
- p_count = (Integer(param_count) rescue -1)
-
- if p_count < 0
- meth_obj.params = "(...)"
- elsif p_count == 0
- meth_obj.params = "()"
- else
- meth_obj.params = "(" +
- (1..p_count).map{|i| "p#{i}"}.join(", ") +
- ")"
- end
-
- if source_file
- file_name = File.join(@file_dir, source_file)
- body = (@@known_bodies[source_file] ||= File.read(file_name))
- else
- body = @body
- end
- if find_body(meth_body, meth_obj, body) and meth_obj.document_self
- class_obj.add_method(meth_obj)
- end
- end
- end
-
- ############################################################
-
- # Find the C code corresponding to a Ruby method
- def find_body(meth_name, meth_obj, body, quiet = false)
- case body
- when %r{((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name}
- \s*(\(.*?\)).*?^}xm
- comment, params = $1, $2
- body_text = $&
-
- remove_private_comments(comment) if comment
-
- # see if we can find the whole body
-
- re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
- if Regexp.new(re, Regexp::MULTILINE).match(body)
- body_text = $&
- end
-
- # The comment block may have been overridden with a
- # 'Document-method' block. This happens in the interpreter
- # when multiple methods are vectored through to the same
- # C method but those methods are logically distinct (for
- # example Kernel.hash and Kernel.object_id share the same
- # implementation
-
- override_comment = find_override_comment(meth_obj.name)
- comment = override_comment if override_comment
-
- find_modifiers(comment, meth_obj) if comment
-
-# meth_obj.params = params
- meth_obj.start_collecting_tokens
- meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text))
- meth_obj.comment = mangle_comment(comment)
- when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- comment = $1
- find_body($2, meth_obj, body, true)
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment) + meth_obj.comment
- when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- unless find_body($1, meth_obj, body, true)
- warn "No definition for #{meth_name}" unless quiet
- return false
- end
- else
-
- # No body, but might still have an override comment
- comment = find_override_comment(meth_obj.name)
-
- if comment
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment)
- else
- warn "No definition for #{meth_name}" unless quiet
- return false
- end
- end
- true
- end
-
-
- ##################################################
- #
- # If the comment block contains a section that looks like
- # call-seq:
- # Array.new
- # Array.new(10)
- # use it for the parameters
- def find_modifiers(comment, meth_obj)
- if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
- meth_obj.document_self = false
- end
- if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
- seq = $1
- seq.gsub!(/^\s*\*\s*/, '')
- meth_obj.call_seq = seq
- end
- end
-
- ############################################################
-
- def find_override_comment(meth_name)
- name = Regexp.escape(meth_name)
- if @body =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m
- $1
- end
- end
-
- ############################################################
-
- # Look for includes of the form
- # rb_include_module(rb_cArray, rb_mEnumerable);
- def do_includes
- @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
- if cls = @classes[c]
- m = @known_classes[m] || m
- cls.add_include(Include.new(m, ""))
- end
- end
- end
-
- ############################################################
-
- # Remove the /*'s and leading asterisks from C comments
-
- def mangle_comment(comment)
- comment.sub!(%r{/\*+}) { " " * $&.length }
- comment.sub!(%r{\*+/}) { " " * $&.length }
- comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
- comment
- end
-
- def find_class(raw_name, name)
- unless @classes[raw_name]
- if raw_name =~ /^rb_m/
- @classes[raw_name] = @top_level.add_module(NormalModule, name)
- else
- @classes[raw_name] = @top_level.add_class(NormalClass, name, nil)
- end
- end
- @classes[raw_name]
- end
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = Options.instance.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
- line
- end .join("\n")
- else
- body
- end
- end
-
- # Remove #ifdefs that would otherwise confuse us
-
- def handle_ifdefs_in(body)
- body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m) { $1 }
- end
-
- end
-
-end
diff --git a/lib/rdoc/parsers/parse_f95.rb b/lib/rdoc/parsers/parse_f95.rb
deleted file mode 100644
index f3f6d76103..0000000000
--- a/lib/rdoc/parsers/parse_f95.rb
+++ /dev/null
@@ -1,1841 +0,0 @@
-#= parse_f95.rb - Fortran95 Parser
-#
-#== Overview
-#
-#"parse_f95.rb" parses Fortran95 files with suffixes "f90", "F90", "f95"
-#and "F95". Fortran95 files are expected to be conformed to Fortran95
-#standards.
-#
-#== Rules
-#
-#Fundamental rules are same as that of the Ruby parser.
-#But comment markers are '!' not '#'.
-#
-#=== Correspondence between RDoc documentation and Fortran95 programs
-#
-#"parse_f95.rb" parses main programs, modules, subroutines, functions,
-#derived-types, public variables, public constants,
-#defined operators and defined assignments.
-#These components are described in items of RDoc documentation, as follows.
-#
-#Files :: Files (same as Ruby)
-#Classes :: Modules
-#Methods :: Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments
-#Required files :: Files in which imported modules, external subroutines and external functions are defined.
-#Included Modules :: List of imported modules
-#Attributes :: List of derived-types, List of imported modules all of whose components are published again
-#
-#Components listed in 'Methods' (subroutines, functions, ...)
-#defined in modules are described in the item of 'Classes'.
-#On the other hand, components defined in main programs or
-#as external procedures are described in the item of 'Files'.
-#
-#=== Components parsed by default
-#
-#By default, documentation on public components (subroutines, functions,
-#variables, constants, derived-types, defined operators,
-#defined assignments) are generated.
-#With "--all" option, documentation on all components
-#are generated (almost same as the Ruby parser).
-#
-#=== Information parsed automatically
-#
-#The following information is automatically parsed.
-#
-#* Types of arguments
-#* Types of variables and constants
-#* Types of variables in the derived types, and initial values
-#* NAMELISTs and types of variables in them, and initial values
-#
-#Aliases by interface statement are described in the item of 'Methods'.
-#
-#Components which are imported from other modules and published again
-#are described in the item of 'Methods'.
-#
-#=== Format of comment blocks
-#
-#Comment blocks should be written as follows.
-#Comment blocks are considered to be ended when the line without '!'
-#appears.
-#The indentation is not necessary.
-#
-# ! (Top of file)
-# !
-# ! Comment blocks for the files.
-# !
-# !--
-# ! The comment described in the part enclosed by
-# ! "!--" and "!++" is ignored.
-# !++
-# !
-# module hogehoge
-# !
-# ! Comment blocks for the modules (or the programs).
-# !
-#
-# private
-#
-# logical :: a ! a private variable
-# real, public :: b ! a public variable
-# integer, parameter :: c = 0 ! a public constant
-#
-# public :: c
-# public :: MULTI_ARRAY
-# public :: hoge, foo
-#
-# type MULTI_ARRAY
-# !
-# ! Comment blocks for the derived-types.
-# !
-# real, pointer :: var(:) =>null() ! Comments block for the variables.
-# integer :: num = 0
-# end type MULTI_ARRAY
-#
-# contains
-#
-# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
-# & out )
-# !
-# ! Comment blocks for the subroutines or functions
-# !
-# character(*),intent(in):: in ! Comment blocks for the arguments.
-# character(*),intent(out),allocatable,target :: in
-# ! Comment blocks can be
-# ! written under Fortran statements.
-#
-# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
-# integer :: id
-#
-# namelist /varinfo_nml/ file, id
-# !
-# ! Comment blocks for the NAMELISTs.
-# ! Information about variables are described above.
-# !
-#
-# ....
-#
-# end subroutine hoge
-#
-# integer function foo( in )
-# !
-# ! This part is considered as comment block.
-#
-# ! Comment blocks under blank lines are ignored.
-# !
-# integer, intent(in):: inA ! This part is considered as comment block.
-#
-# ! This part is ignored.
-#
-# end function foo
-#
-# subroutine hide( in, &
-# & out ) !:nodoc:
-# !
-# ! If "!:nodoc:" is described at end-of-line in subroutine
-# ! statement as above, the subroutine is ignored.
-# ! This assignment can be used to modules, subroutines,
-# ! functions, variables, constants, derived-types,
-# ! defined operators, defined assignments,
-# ! list of imported modules ("use" statement).
-# !
-#
-# ....
-#
-# end subroutine hide
-#
-# end module hogehoge
-#
-
-
-require "rdoc/code_objects"
-
-module RDoc
-
- class Token
-
- NO_TEXT = "??".freeze
-
- def initialize(line_no, char_no)
- @line_no = line_no
- @char_no = char_no
- @text = NO_TEXT
- end
- # Because we're used in contexts that expect to return a token,
- # we set the text string and then return ourselves
- def set_text(text)
- @text = text
- self
- end
-
- attr_reader :line_no, :char_no, :text
-
- end
-
- # See rdoc/parsers/parse_f95.rb
-
- class Fortran95parser
-
- extend ParserFactory
- parse_files_matching(/\.((f|F)9(0|5)|F)$/)
-
- @@external_aliases = []
- @@public_methods = []
-
- # "false":: Comments are below source code
- # "true" :: Comments are upper source code
- COMMENTS_ARE_UPPER = false
-
- # Internal alias message
- INTERNAL_ALIAS_MES = "Alias for"
-
- # External alias message
- EXTERNAL_ALIAS_MES = "The entity is"
-
- # prepare to parse a Fortran 95 file
- def initialize(top_level, file_name, body, options, stats)
- @body = body
- @stats = stats
- @file_name = file_name
- @options = options
- @top_level = top_level
- @progress = $stderr unless options.quiet
- end
-
- # devine code constructs
- def scan
-
- # remove private comment
- remaining_code = remove_private_comments(@body)
-
- # continuation lines are united to one line
- remaining_code = united_to_one_line(remaining_code)
-
- # semicolons are replaced to line feed
- remaining_code = semicolon_to_linefeed(remaining_code)
-
- # collect comment for file entity
- whole_comment, remaining_code = collect_first_comment(remaining_code)
- @top_level.comment = whole_comment
-
- # String "remaining_code" is converted to Array "remaining_lines"
- remaining_lines = remaining_code.split("\n")
-
- # "module" or "program" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- module_program_trailing = ""
- module_program_name = ""
- other_block_level_depth = 0
- other_block_searching_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag && !other_block_searching_flag
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
- block_searching_flag = :module
- block_searching_lines << line
- module_program_name = $1
- module_program_trailing = find_comments($2)
- next false
- elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?\w/ && !block_start?(line)
- block_searching_flag = :program
- block_searching_lines << line
- module_program_name = $1 || ""
- module_program_trailing = find_comments($2)
- next false
-
- elsif block_start?(line)
- other_block_searching_flag = true
- next line
-
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- elsif other_block_searching_flag
- other_block_level_depth += 1 if block_start?(line)
- other_block_level_depth -= 1 if block_end?(line)
- if other_block_level_depth < 0
- other_block_level_depth = 0
- other_block_searching_flag = nil
- end
- next line
- end
-
- block_searching_lines << line
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "module_program_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- module_program_code = block_searching_lines.join("\n")
- module_program_code = remove_empty_head_lines(module_program_code)
- if module_program_trailing =~ /^:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- end
-
- # NormalClass is created, and added to @top_level
- #
- if block_searching_flag == :module
- module_name = module_program_name
- module_code = module_program_code
- module_trailing = module_program_trailing
- progress "m"
- @stats.num_modules += 1
- f9x_module = @top_level.add_module NormalClass, module_name
- f9x_module.record_location @top_level
-
- f9x_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
- module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
- f9x_module.comment = f9x_comment
- parse_program_or_module(f9x_module, module_code)
-
- TopLevel.all_files.each do |name, toplevel|
- if toplevel.include_includes?(module_name, @options.ignore_case)
- if !toplevel.include_requires?(@file_name, @options.ignore_case)
- toplevel.add_require(Require.new(@file_name, ""))
- end
- end
- toplevel.each_classmodule{|m|
- if m.include_includes?(module_name, @options.ignore_case)
- if !m.include_requires?(@file_name, @options.ignore_case)
- m.add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
- elsif block_searching_flag == :program
- program_name = module_program_name
- program_code = module_program_code
- program_trailing = module_program_trailing
- progress "p"
- program_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
- program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
- program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
- + program_comment
- @top_level.comment << program_comment
- parse_program_or_module(@top_level, program_code, :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- }
-
- remaining_lines.delete_if{ |line|
- line == false
- }
-
- # External subprograms and functions are parsed
- #
- parse_program_or_module(@top_level, remaining_lines.join("\n"),
- :public, true)
-
- @top_level
- end # End of scan
-
- private
-
- def parse_program_or_module(container, code,
- visibility=:public, external=nil)
- return unless container
- return unless code
- remaining_lines = code.split("\n")
- remaining_code = "#{code}"
-
- #
- # Parse variables before "contains" in module
- #
- level_depth = 0
- before_contains_lines = []
- before_contains_code = nil
- before_contains_flag = nil
- remaining_lines.each{ |line|
- if !before_contains_flag
- if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
- before_contains_flag = true
- end
- else
- break if line =~ /^\s*?contains\s*?(!.*?)?$/i
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- break if level_depth < 0
- before_contains_lines << line
- end
- }
- before_contains_code = before_contains_lines.join("\n")
- if before_contains_code
- before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
- before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- end
-
- #
- # Parse global "use"
- #
- use_check_code = "#{before_contains_code}"
- cascaded_modules_list = []
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_list = $2 || ""
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- progress "."
- container.add_include Include.new(used_mod_name, "")
- end
- if ! (used_list =~ /\,\s*?only\s*?:/i )
- cascaded_modules_list << "\#" + used_mod_name
- end
- end
-
- #
- # Parse public and private, and store information.
- # This information is used when "add_method" and
- # "set_visibility_for" are called.
- #
- visibility_default, visibility_info =
- parse_visibility(remaining_lines.join("\n"), visibility, container)
- @@public_methods.concat visibility_info
- if visibility_default == :public
- if !cascaded_modules_list.empty?
- cascaded_modules =
- Attr.new("Cascaded Modules",
- "Imported modules all of whose components are published again",
- "",
- cascaded_modules_list.join(", "))
- container.add_attribute(cascaded_modules)
- end
- end
-
- #
- # Check rename elements
- #
- use_check_code = "#{before_contains_code}"
- while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
- used_elements.split(",").each{ |used|
- if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
- local = $1
- org = $2
- @@public_methods.collect!{ |pub_meth|
- if local == pub_meth["name"] ||
- local.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- pub_meth["name"] = org
- pub_meth["local_name"] = local
- end
- pub_meth
- }
- end
- }
- end
-
- #
- # Parse private "use"
- #
- use_check_code = remaining_lines.join("\n")
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- progress "."
- container.add_include Include.new(used_mod_name, "")
- end
- end
-
- container.each_includes{ |inc|
- TopLevel.all_files.each do |name, toplevel|
- indicated_mod = toplevel.find_symbol(inc.name,
- nil, @options.ignore_case)
- if indicated_mod
- indicated_name = indicated_mod.parent.file_relative_name
- if !container.include_requires?(indicated_name, @options.ignore_case)
- container.add_require(Require.new(indicated_name, ""))
- end
- break
- end
- end
- }
-
- #
- # Parse derived-types definitions
- #
- derived_types_comment = ""
- remaining_code = remaining_lines.join("\n")
- while remaining_code =~ /^\s*?
- type[\s\,]+(public|private)?\s*?(::)?\s*?
- (\w+)\s*?(!.*?)?$
- (.*?)
- ^\s*?end\s+type.*?$
- /imx
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- typename = $3.chomp.strip
- type_elements = $5 || ""
- type_code = remove_empty_head_lines($&)
- type_trailing = find_comments($4)
- next if type_trailing =~ /^:nodoc:/
- type_visibility = $1
- type_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) + "\n" + type_trailing :
- type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
- type_element_visibility_public = true
- type_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- type_element_visibility_public = nil
- break
- end
- } if type_code
-
- args_comment = ""
- type_args_info = nil
-
- if @options.show_all
- args_comment = find_arguments(nil, type_code, true)
- else
- type_public_args_list = []
- type_args_info = definition_info(type_code)
- type_args_info.each{ |arg|
- arg_is_public = type_element_visibility_public
- arg_is_public = true if arg.include_attr?("public")
- arg_is_public = nil if arg.include_attr?("private")
- type_public_args_list << arg.varname if arg_is_public
- }
- args_comment = find_arguments(type_public_args_list, type_code)
- end
-
- type = AnyMethod.new("type #{typename}", typename)
- type.singleton = false
- type.params = ""
- type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
- type.comment << args_comment if args_comment
- type.comment << type_comment if type_comment
- progress "t"
- @stats.num_methods += 1
- container.add_method type
-
- set_visibility(container, typename, visibility_default, @@public_methods)
-
- if type_visibility
- type_visibility.gsub!(/\s/,'')
- type_visibility.gsub!(/\,/,'')
- type_visibility.gsub!(/:/,'')
- type_visibility.downcase!
- if type_visibility == "public"
- container.set_visibility_for([typename], :public)
- elsif type_visibility == "private"
- container.set_visibility_for([typename], :private)
- end
- end
-
- check_public_methods(type, container.name)
-
- if @options.show_all
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- else
- if type.visibility == :public
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- end
- end
-
- end
-
- if !derived_types_comment.empty?
- derived_types_table =
- Attr.new("Derived Types", "Derived_Types", "",
- derived_types_comment)
- container.add_attribute(derived_types_table)
- end
-
- #
- # move interface scope
- #
- interface_code = ""
- while remaining_code =~ /^\s*?
- interface(
- \s+\w+ |
- \s+operator\s*?\(.*?\) |
- \s+assignment\s*?\(\s*?=\s*?\)
- )?\s*?$
- (.*?)
- ^\s*?end\s+interface.*?$
- /imx
- interface_code << remove_empty_head_lines($&) + "\n"
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- end
-
- #
- # Parse global constants or variables in modules
- #
- const_var_defs = definition_info(before_contains_code)
- const_var_defs.each{|defitem|
- next if defitem.nodoc
- const_or_var_type = "Variable"
- const_or_var_progress = "v"
- if defitem.include_attr?("parameter")
- const_or_var_type = "Constant"
- const_or_var_progress = "c"
- end
- const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
- const_or_var.singleton = false
- const_or_var.params = ""
- self_comment = find_arguments([defitem.varname], before_contains_code)
- const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
- const_or_var.comment << self_comment if self_comment
- progress const_or_var_progress
- @stats.num_methods += 1
- container.add_method const_or_var
-
- set_visibility(container, defitem.varname, visibility_default, @@public_methods)
-
- if defitem.include_attr?("public")
- container.set_visibility_for([defitem.varname], :public)
- elsif defitem.include_attr?("private")
- container.set_visibility_for([defitem.varname], :private)
- end
-
- check_public_methods(const_or_var, container.name)
-
- } if const_var_defs
-
- remaining_lines = remaining_code.split("\n")
-
- # "subroutine" or "function" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag
- # subroutine
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :subroutine
- block_searching_lines << line
-
- procedure_name = $2.chomp.strip
- procedure_params = $3 || ""
- procedure_prefix = $1 || ""
- procedure_trailing = $4 || "!"
- next false
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :function
- block_searching_lines << line
-
- procedure_prefix = $1 || ""
- procedure_type = $2 ? $2.chomp.strip : nil
- procedure_name = $8.chomp.strip
- procedure_params = $9 || ""
- procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
- procedure_trailing = $12 || "!"
- next false
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- end
- contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
- block_searching_lines << line
- contains_lines << line if contains_flag
-
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "procedure_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- procedure_code = block_searching_lines.join("\n")
- procedure_code = remove_empty_head_lines(procedure_code)
- if procedure_trailing =~ /^!:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- next false
- end
-
- # AnyMethod is created, and added to container
- #
- subroutine_function = nil
- if block_searching_flag == :subroutine
- subroutine_prefix = procedure_prefix
- subroutine_name = procedure_name
- subroutine_params = procedure_params
- subroutine_trailing = procedure_trailing
- subroutine_code = procedure_code
-
- subroutine_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + subroutine_trailing :
- subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
- subroutine = AnyMethod.new("subroutine", subroutine_name)
- parse_subprogram(subroutine, subroutine_params,
- subroutine_comment, subroutine_code,
- before_contains_code, nil, subroutine_prefix)
- progress "s"
- @stats.num_methods += 1
- container.add_method subroutine
- subroutine_function = subroutine
-
- elsif block_searching_flag == :function
- function_prefix = procedure_prefix
- function_type = procedure_type
- function_name = procedure_name
- function_params_org = procedure_params
- function_result_arg = procedure_result_arg
- function_trailing = procedure_trailing
- function_code_org = procedure_code
-
- function_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + function_trailing :
- function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
-
- function_code = "#{function_code_org}"
- if function_type
- function_code << "\n" + function_type + " :: " + function_result_arg
- end
-
- function_params =
- function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
-
- function = AnyMethod.new("function", function_name)
- parse_subprogram(function, function_params,
- function_comment, function_code,
- before_contains_code, true, function_prefix)
-
- # Specific modification due to function
- function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
- function.params << " result(" + function_result_arg + ")"
- function.start_collecting_tokens
- function.add_token Token.new(1,1).set_text(function_code_org)
-
- progress "f"
- @stats.num_methods += 1
- container.add_method function
- subroutine_function = function
-
- end
-
- # The visibility of procedure is specified
- #
- set_visibility(container, procedure_name,
- visibility_default, @@public_methods)
-
- # The alias for this procedure from external modules
- #
- check_external_aliases(procedure_name,
- subroutine_function.params,
- subroutine_function.comment, subroutine_function) if external
- check_public_methods(subroutine_function, container.name)
-
-
- # contains_lines are parsed as private procedures
- if contains_flag
- parse_program_or_module(container,
- contains_lines.join("\n"), :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- contains_lines = []
- contains_flag = nil
- next false
- } # End of remaining_lines.collect!{|line|
-
- # Array remains_lines is converted to String remains_code again
- #
- remaining_code = remaining_lines.join("\n")
-
- #
- # Parse interface
- #
- interface_scope = false
- generic_name = ""
- interface_code.split("\n").each{ |line|
- if /^\s*?
- interface(
- \s+\w+|
- \s+operator\s*?\(.*?\)|
- \s+assignment\s*?\(\s*?=\s*?\)
- )?
- \s*?(!.*?)?$
- /ix =~ line
- generic_name = $1 ? $1.strip.chomp : nil
- interface_trailing = $2 || "!"
- interface_scope = true
- interface_scope = false if interface_trailing =~ /!:nodoc:/
-# if generic_name =~ /operator\s*?\((.*?)\)/i
-# operator_name = $1
-# if operator_name && !operator_name.empty?
-# generic_name = "#{operator_name}"
-# end
-# end
-# if generic_name =~ /assignment\s*?\((.*?)\)/i
-# assignment_name = $1
-# if assignment_name && !assignment_name.empty?
-# generic_name = "#{assignment_name}"
-# end
-# end
- end
- if /^\s*?end\s+interface/i =~ line
- interface_scope = false
- generic_name = nil
- end
- # internal alias
- if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
- procedures = $1.strip.chomp
- procedures_trailing = $2 || "!"
- next if procedures_trailing =~ /!:nodoc:/
- procedures.split(",").each{ |proc|
- proc.strip!
- proc.chomp!
- next if generic_name == proc || !generic_name
- old_meth = container.find_symbol(proc, nil, @options.ignore_case)
- next if !old_meth
- nolink = old_meth.visibility == :private ? true : nil
- nolink = nil if @options.show_all
- new_meth =
- initialize_external_method(generic_name, proc,
- old_meth.params, nil,
- old_meth.comment,
- old_meth.clone.token_stream[0].text,
- true, nolink)
- new_meth.singleton = old_meth.singleton
-
- progress "i"
- @stats.num_methods += 1
- container.add_method new_meth
-
- set_visibility(container, generic_name, visibility_default, @@public_methods)
-
- check_public_methods(new_meth, container.name)
-
- }
- end
-
- # external aliases
- if interface_scope
- # subroutine
- proc = nil
- params = nil
- procedures_trailing = nil
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- proc = $2.chomp.strip
- generic_name = proc unless generic_name
- params = $3 || ""
- procedures_trailing = $4 || "!"
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- proc = $8.chomp.strip
- generic_name = proc unless generic_name
- params = $9 || ""
- procedures_trailing = $12 || "!"
- else
- next
- end
- next if procedures_trailing =~ /!:nodoc:/
- indicated_method = nil
- indicated_file = nil
- TopLevel.all_files.each do |name, toplevel|
- indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
- indicated_file = name
- break if indicated_method
- end
-
- if indicated_method
- external_method =
- initialize_external_method(generic_name, proc,
- indicated_method.params,
- indicated_file,
- indicated_method.comment)
-
- progress "e"
- @stats.num_methods += 1
- container.add_method external_method
- set_visibility(container, generic_name, visibility_default, @@public_methods)
- if !container.include_requires?(indicated_file, @options.ignore_case)
- container.add_require(Require.new(indicated_file, ""))
- end
- check_public_methods(external_method, container.name)
-
- else
- @@external_aliases << {
- "new_name" => generic_name,
- "old_name" => proc,
- "file_or_module" => container,
- "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
- }
- end
- end
-
- } if interface_code # End of interface_code.split("\n").each ...
-
- #
- # Already imported methods are removed from @@public_methods.
- # Remainders are assumed to be imported from other modules.
- #
- @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
-
- @@public_methods.each{ |pub_meth|
- next unless pub_meth["file_or_module"].name == container.name
- pub_meth["used_modules"].each{ |used_mod|
- TopLevel.all_classes_and_modules.each{ |modules|
- if modules.name == used_mod ||
- modules.name.upcase == used_mod.upcase &&
- @options.ignore_case
- modules.method_list.each{ |meth|
- if meth.name == pub_meth["name"] ||
- meth.name.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- new_meth = initialize_public_method(meth,
- modules.name)
- if pub_meth["local_name"]
- new_meth.name = pub_meth["local_name"]
- end
- progress "e"
- @stats.num_methods += 1
- container.add_method new_meth
- end
- }
- end
- }
- }
- }
-
- container
- end # End of parse_program_or_module
-
- #
- # Parse arguments, comment, code of subroutine and function.
- # Return AnyMethod object.
- #
- def parse_subprogram(subprogram, params, comment, code,
- before_contains=nil, function=nil, prefix=nil)
- subprogram.singleton = false
- prefix = "" if !prefix
- arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
- args_comment, params_opt =
- find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
- nil, nil, true)
- params_opt = "( " + params_opt + " ) " if params_opt
- subprogram.params = params_opt || ""
- namelist_comment = find_namelists(code, before_contains)
-
- block_comment = find_comments comment
- if function
- subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
- else
- subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
- end
- subprogram.comment << args_comment if args_comment
- subprogram.comment << block_comment if block_comment
- subprogram.comment << namelist_comment if namelist_comment
-
- # For output source code
- subprogram.start_collecting_tokens
- subprogram.add_token Token.new(1,1).set_text(code)
-
- subprogram
- end
-
- #
- # Collect comment for file entity
- #
- def collect_first_comment(body)
- comment = ""
- not_comment = ""
- comment_start = false
- comment_end = false
- body.split("\n").each{ |line|
- if comment_end
- not_comment << line
- not_comment << "\n"
- elsif /^\s*?!\s?(.*)$/i =~ line
- comment_start = true
- comment << $1
- comment << "\n"
- elsif /^\s*?$/i =~ line
- comment_end = true if comment_start && COMMENTS_ARE_UPPER
- else
- comment_end = true
- not_comment << line
- not_comment << "\n"
- end
- }
- return comment, not_comment
- end
-
-
- # Return comments of definitions of arguments
- #
- # If "all" argument is true, information of all arguments are returned.
- # If "modified_params" is true, list of arguments are decorated,
- # for exameple, optional arguments are parenthetic as "[arg]".
- #
- def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
- return unless args || all
- indent = "" unless indent
- args = ["all"] if all
- params = "" if modified_params
- comma = ""
- return unless text
- args_rdocforms = "\n"
- remaining_lines = "#{text}"
- definitions = definition_info(remaining_lines)
- args.each{ |arg|
- arg.strip!
- arg.chomp!
- definitions.each { |defitem|
- if arg == defitem.varname.strip.chomp || all
- args_rdocforms << <<-"EOF"
-
-#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
-#{indent} <tt>#{defitem.types.chomp.strip}</tt>
-EOF
- if !defitem.comment.chomp.strip.empty?
- comment = ""
- defitem.comment.split("\n").each{ |line|
- comment << " " + line + "\n"
- }
- args_rdocforms << <<-"EOF"
-
-#{indent} <tt></tt> ::
-#{indent} <tt></tt>
-#{indent} #{comment.chomp.strip}
-EOF
- end
-
- if modified_params
- if defitem.include_attr?("optional")
- params << "#{comma}[#{arg}]"
- else
- params << "#{comma}#{arg}"
- end
- comma = ", "
- end
- end
- }
- }
- if modified_params
- return args_rdocforms, params
- else
- return args_rdocforms
- end
- end
-
- # Return comments of definitions of namelists
- #
- def find_namelists(text, before_contains=nil)
- return nil if !text
- result = ""
- lines = "#{text}"
- before_contains = "" if !before_contains
- while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
- lines = $~.post_match
- nml_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) : find_comments($~.post_match)
- nml_name = $1
- nml_args = $2.split(",")
- result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
- result << nml_comment + "\n" if nml_comment
- if lines.split("\n")[0] =~ /^\//i
- lines = "namelist " + lines
- end
- result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
- end
- return result
- end
-
- #
- # Comments just after module or subprogram, or arguments are
- # returnd. If "COMMENTS_ARE_UPPER" is true, comments just before
- # modules or subprograms are returnd
- #
- def find_comments text
- return "" unless text
- lines = text.split("\n")
- lines.reverse! if COMMENTS_ARE_UPPER
- comment_block = Array.new
- lines.each do |line|
- break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
- if COMMENTS_ARE_UPPER
- comment_block.unshift line.sub(/^\s*?!\s?/,"")
- else
- comment_block.push line.sub(/^\s*?!\s?/,"")
- end
- end
- nice_lines = comment_block.join("\n").split "\n\s*?\n"
- nice_lines[0] ||= ""
- nice_lines.shift
- end
-
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
- end
- end
-
- #
- # Create method for internal alias
- #
- def initialize_public_method(method, parent)
- return if !method || !parent
-
- new_meth = AnyMethod.new("External Alias for module", method.name)
- new_meth.singleton = method.singleton
- new_meth.params = method.params.clone
- new_meth.comment = remove_trailing_alias(method.comment.clone)
- new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
-
- return new_meth
- end
-
- #
- # Create method for external alias
- #
- # If argument "internal" is true, file is ignored.
- #
- def initialize_external_method(new, old, params, file, comment, token=nil,
- internal=nil, nolink=nil)
- return nil unless new || old
-
- if internal
- external_alias_header = "#{INTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + old
- elsif file
- external_alias_header = "#{EXTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + file + "#" + old
- else
- return nil
- end
- external_meth = AnyMethod.new(external_alias_text, new)
- external_meth.singleton = false
- external_meth.params = params
- external_comment = remove_trailing_alias(comment) + "\n\n" if comment
- external_meth.comment = external_comment || ""
- if nolink && token
- external_meth.start_collecting_tokens
- external_meth.add_token Token.new(1,1).set_text(token)
- else
- external_meth.comment << external_alias_text
- end
-
- return external_meth
- end
-
-
-
- #
- # Parse visibility
- #
- def parse_visibility(code, default, container)
- result = []
- visibility_default = default || :public
-
- used_modules = []
- container.includes.each{|i| used_modules << i.name} if container
-
- remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- visibility_default = :private
- break
- end
- } if remaining_code
-
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :private,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :public,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- end
- } if remaining_code
-
- if container
- result.each{ |vis_info|
- vis_info["parent"] = container.name
- }
- end
-
- return visibility_default, result
- end
-
- #
- # Set visibility
- #
- # "subname" element of "visibility_info" is deleted.
- #
- def set_visibility(container, subname, visibility_default, visibility_info)
- return unless container || subname || visibility_default || visibility_info
- not_found = true
- visibility_info.collect!{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["file_or_module"].name == container.name
- container.set_visibility_for([subname], info["visibility"])
- info["entity_is_discovered"] = true
- not_found = false
- end
- end
- info
- }
- if not_found
- return container.set_visibility_for([subname], visibility_default)
- else
- return container
- end
- end
-
- #
- # Find visibility
- #
- def find_visibility(container, subname, visibility_info)
- return nil if !subname || !visibility_info
- visibility_info.each{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["parent"] == container.name
- return info["visibility"]
- end
- end
- }
- return nil
- end
-
- #
- # Check external aliases
- #
- def check_external_aliases(subname, params, comment, test=nil)
- @@external_aliases.each{ |alias_item|
- if subname == alias_item["old_name"] ||
- subname.upcase == alias_item["old_name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_external_method(alias_item["new_name"],
- subname, params, @file_name,
- comment)
- new_meth.visibility = alias_item["visibility"]
-
- progress "e"
- @stats.num_methods += 1
- alias_item["file_or_module"].add_method(new_meth)
-
- if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
- alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
-
- #
- # Check public_methods
- #
- def check_public_methods(method, parent)
- return if !method || !parent
- @@public_methods.each{ |alias_item|
- parent_is_used_module = nil
- alias_item["used_modules"].each{ |used_module|
- if used_module == parent ||
- used_module.upcase == parent.upcase &&
- @options.ignore_case
- parent_is_used_module = true
- end
- }
- next if !parent_is_used_module
-
- if method.name == alias_item["name"] ||
- method.name.upcase == alias_item["name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_public_method(method, parent)
- if alias_item["local_name"]
- new_meth.name = alias_item["local_name"]
- end
-
- progress "e"
- @stats.num_methods += 1
- alias_item["file_or_module"].add_method new_meth
- end
- }
- end
-
- #
- # Continuous lines are united.
- #
- # Comments in continuous lines are removed.
- #
- def united_to_one_line(f90src)
- return "" unless f90src
- lines = f90src.split("\n")
- previous_continuing = false
- now_continuing = false
- body = ""
- lines.each{ |line|
- words = line.split("")
- next if words.empty? && previous_continuing
- commentout = false
- brank_flag = true ; brank_char = ""
- squote = false ; dquote = false
- ignore = false
- words.collect! { |char|
- if previous_continuing && brank_flag
- now_continuing = true
- ignore = true
- case char
- when "!" ; break
- when " " ; brank_char << char ; next ""
- when "&"
- brank_flag = false
- now_continuing = false
- next ""
- else
- brank_flag = false
- now_continuing = false
- ignore = false
- next brank_char + char
- end
- end
- ignore = false
-
- if now_continuing
- next ""
- elsif !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when "&" ; now_continuing = true ; next ""
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- if !ignore && !previous_continuing || !brank_flag
- if previous_continuing
- body << words.join("")
- else
- body << "\n" + words.join("")
- end
- end
- previous_continuing = now_continuing ? true : nil
- now_continuing = nil
- }
- return body
- end
-
-
- #
- # Continuous line checker
- #
- def continuous_line?(line)
- continuous = false
- if /&\s*?(!.*)?$/ =~ line
- continuous = true
- if comment_out?($~.pre_match)
- continuous = false
- end
- end
- return continuous
- end
-
- #
- # Comment out checker
- #
- def comment_out?(line)
- return nil unless line
- commentout = false
- squote = false ; dquote = false
- line.split("").each { |char|
- if !(squote) && !(dquote)
- case char
- when "!" ; commentout = true ; break
- when "\""; dquote = true
- when "\'"; squote = true
- else next
- end
- elsif squote
- case char
- when "\'"; squote = false
- else next
- end
- elsif dquote
- case char
- when "\""; dquote = false
- else next
- end
- end
- }
- return commentout
- end
-
- #
- # Semicolons are replaced to line feed.
- #
- def semicolon_to_linefeed(text)
- return "" unless text
- lines = text.split("\n")
- lines.collect!{ |line|
- words = line.split("")
- commentout = false
- squote = false ; dquote = false
- words.collect! { |char|
- if !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when ";" ; "\n"
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- words.join("")
- }
- return lines.join("\n")
- end
-
- #
- # Which "line" is start of block (module, program, block data,
- # subroutine, function) statement ?
- #
- def block_start?(line)
- return nil if !line
-
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- return true
- end
-
- return nil
- end
-
- #
- # Which "line" is end of block (module, program, block data,
- # subroutine, function) statement ?
- #
- def block_end?(line)
- return nil if !line
-
- if line =~ /^\s*?end\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
- return true
- end
-
- return nil
- end
-
- #
- # Remove "Alias for" in end of comments
- #
- def remove_trailing_alias(text)
- return "" if !text
- lines = text.split("\n").reverse
- comment_block = Array.new
- checked = false
- lines.each do |line|
- if !checked
- if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
- /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
- checked = true
- next
- end
- end
- comment_block.unshift line
- end
- nice_lines = comment_block.join("\n")
- nice_lines ||= ""
- return nice_lines
- end
-
- # Empty lines in header are removed
- def remove_empty_head_lines(text)
- return "" unless text
- lines = text.split("\n")
- header = true
- lines.delete_if{ |line|
- header = false if /\S/ =~ line
- header && /^\s*?$/ =~ line
- }
- lines.join("\n")
- end
-
-
- # header marker "=", "==", ... are removed
- def remove_header_marker(text)
- return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
- end
-
- def remove_private_comments(body)
- body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
- return body
- end
-
-
- #
- # Information of arguments of subroutines and functions in Fortran95
- #
- class Fortran95Definition
-
- # Name of variable
- #
- attr_reader :varname
-
- # Types of variable
- #
- attr_reader :types
-
- # Initial Value
- #
- attr_reader :inivalue
-
- # Suffix of array
- #
- attr_reader :arraysuffix
-
- # Comments
- #
- attr_accessor :comment
-
- # Flag of non documentation
- #
- attr_accessor :nodoc
-
- def initialize(varname, types, inivalue, arraysuffix, comment,
- nodoc=false)
- @varname = varname
- @types = types
- @inivalue = inivalue
- @arraysuffix = arraysuffix
- @comment = comment
- @nodoc = nodoc
- end
-
- def to_s
- return <<-EOF
-<Fortran95Definition:
- varname=#{@varname}, types=#{types},
- inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
- comment=
-#{@comment}
->
-EOF
- end
-
- #
- # If attr is included, true is returned
- #
- def include_attr?(attr)
- return if !attr
- @types.split(",").each{ |type|
- return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
- }
- return nil
- end
-
- end # End of Fortran95Definition
-
- #
- # Parse string argument "text", and Return Array of
- # Fortran95Definition object
- #
- def definition_info(text)
- return nil unless text
- lines = "#{text}"
- defs = Array.new
- comment = ""
- trailing_comment = ""
- under_comment_valid = false
- lines.split("\n").each{ |line|
- if /^\s*?!\s?(.*)/ =~ line
- if COMMENTS_ARE_UPPER
- comment << remove_header_marker($1)
- comment << "\n"
- elsif defs[-1] && under_comment_valid
- defs[-1].comment << "\n"
- defs[-1].comment << remove_header_marker($1)
- end
- next
- elsif /^\s*?$/ =~ line
- comment = ""
- under_comment_valid = false
- next
- end
- type = ""
- characters = ""
- if line =~ /^\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | type\s*?\([\w\s]+?\)[\s\,]*
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | double\s+precision[\s\,]*
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- )
- (.*?::)?
- (.+)$
- /ix
- characters = $8
- type = $1
- type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
- else
- under_comment_valid = false
- next
- end
- squote = false ; dquote = false ; bracket = 0
- iniflag = false; commentflag = false
- varname = "" ; arraysuffix = "" ; inivalue = ""
- start_pos = defs.size
- characters.split("").each { |char|
- if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
- case char
- when "!" ; commentflag = true
- when "(" ; bracket += 1 ; arraysuffix = char
- when "\""; dquote = true
- when "\'"; squote = true
- when "=" ; iniflag = true ; inivalue << char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- under_comment_valid = true
- when " " ; next
- else ; varname << char
- end
- elsif commentflag
- comment << remove_header_marker(char)
- trailing_comment << remove_header_marker(char)
- elsif iniflag
- if dquote
- case char
- when "\"" ; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif squote
- case char
- when "\'" ; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif bracket > 0
- case char
- when "(" ; bracket += 1 ; inivalue << char
- when ")" ; bracket -= 1 ; inivalue << char
- else ; inivalue << char
- end
- else
- case char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- iniflag = false
- under_comment_valid = true
- when "(" ; bracket += 1 ; inivalue << char
- when "\""; dquote = true ; inivalue << char
- when "\'"; squote = true ; inivalue << char
- when "!" ; commentflag = true
- else ; inivalue << char
- end
- end
- elsif !(squote) && !(dquote) && bracket > 0
- case char
- when "(" ; bracket += 1 ; arraysuffix << char
- when ")" ; bracket -= 1 ; arraysuffix << char
- else ; arraysuffix << char
- end
- elsif squote
- case char
- when "\'"; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- end
- }
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- if trailing_comment =~ /^:nodoc:/
- defs[start_pos..-1].collect!{ |defitem|
- defitem.nodoc = true
- }
- end
- varname = "" ; arraysuffix = "" ; inivalue = ""
- comment = ""
- under_comment_valid = true
- trailing_comment = ""
- }
- return defs
- end
-
-
- end # class Fortran95parser
-
-end # module RDoc
diff --git a/lib/rdoc/parsers/parse_rb.rb b/lib/rdoc/parsers/parse_rb.rb
deleted file mode 100644
index dde017be7d..0000000000
--- a/lib/rdoc/parsers/parse_rb.rb
+++ /dev/null
@@ -1,2605 +0,0 @@
-#!/usr/local/bin/ruby
-
-# Parse a Ruby source file, building a set of objects
-# representing the modules, classes, methods,
-# requires, and includes we find (these classes
-# are defined in code_objects.rb).
-
-# This file contains stuff stolen outright from:
-#
-# rtags.rb -
-# ruby-lex.rb - ruby lexcal analizer
-# ruby-token.rb - ruby tokens
-# by Keiju ISHITSUKA (Nippon Rational Inc.)
-#
-
-require "e2mmap"
-require "irb/slex"
-
-require "rdoc/code_objects"
-require "rdoc/tokenstream"
-
-require "rdoc/markup/simple_markup/preprocess"
-
-require "rdoc/parsers/parserfactory"
-
-$TOKEN_DEBUG = $DEBUG
-
-# Definitions of all tokens involved in the lexical analysis
-
-module RubyToken
- EXPR_BEG = :EXPR_BEG
- EXPR_MID = :EXPR_MID
- EXPR_END = :EXPR_END
- EXPR_ARG = :EXPR_ARG
- EXPR_FNAME = :EXPR_FNAME
- EXPR_DOT = :EXPR_DOT
- EXPR_CLASS = :EXPR_CLASS
-
- class Token
- NO_TEXT = "??".freeze
- attr :text
-
- def initialize(line_no, char_no)
- @line_no = line_no
- @char_no = char_no
- @text = NO_TEXT
- end
-
- # Because we're used in contexts that expect to return a token,
- # we set the text string and then return ourselves
- def set_text(text)
- @text = text
- self
- end
-
- attr_reader :line_no, :char_no, :text
- end
-
- class TkNode < Token
- attr :node
- end
-
- class TkId < Token
- def initialize(line_no, char_no, name)
- super(line_no, char_no)
- @name = name
- end
- attr :name
- end
-
- class TkKW < TkId
- end
-
- class TkVal < Token
- def initialize(line_no, char_no, value = nil)
- super(line_no, char_no)
- set_text(value)
- end
- end
-
- class TkOp < Token
- def name
- self.class.op_name
- end
- end
-
- class TkOPASGN < TkOp
- def initialize(line_no, char_no, op)
- super(line_no, char_no)
- op = TkReading2Token[op] unless op.kind_of?(Symbol)
- @op = op
- end
- attr :op
- end
-
- class TkUnknownChar < Token
- def initialize(line_no, char_no, id)
- super(line_no, char_no)
- @name = char_no.chr
- end
- attr :name
- end
-
- class TkError < Token
- end
-
- def set_token_position(line, char)
- @prev_line_no = line
- @prev_char_no = char
- end
-
- def Token(token, value = nil)
- tk = nil
- case token
- when String, Symbol
- source = token.kind_of?(String) ? TkReading2Token : TkSymbol2Token
- if (tk = source[token]).nil?
- IRB.fail TkReading2TokenNoKey, token
- end
- tk = Token(tk[0], value)
- else
- tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
- token.new(@prev_line_no, @prev_char_no)
- else
- token.new(@prev_line_no, @prev_char_no, value)
- end
- end
- tk
- end
-
- TokenDefinitions = [
- [:TkCLASS, TkKW, "class", EXPR_CLASS],
- [:TkMODULE, TkKW, "module", EXPR_BEG],
- [:TkDEF, TkKW, "def", EXPR_FNAME],
- [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
- [:TkBEGIN, TkKW, "begin", EXPR_BEG],
- [:TkRESCUE, TkKW, "rescue", EXPR_MID],
- [:TkENSURE, TkKW, "ensure", EXPR_BEG],
- [:TkEND, TkKW, "end", EXPR_END],
- [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
- [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
- [:TkTHEN, TkKW, "then", EXPR_BEG],
- [:TkELSIF, TkKW, "elsif", EXPR_BEG],
- [:TkELSE, TkKW, "else", EXPR_BEG],
- [:TkCASE, TkKW, "case", EXPR_BEG],
- [:TkWHEN, TkKW, "when", EXPR_BEG],
- [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
- [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
- [:TkFOR, TkKW, "for", EXPR_BEG],
- [:TkBREAK, TkKW, "break", EXPR_END],
- [:TkNEXT, TkKW, "next", EXPR_END],
- [:TkREDO, TkKW, "redo", EXPR_END],
- [:TkRETRY, TkKW, "retry", EXPR_END],
- [:TkIN, TkKW, "in", EXPR_BEG],
- [:TkDO, TkKW, "do", EXPR_BEG],
- [:TkRETURN, TkKW, "return", EXPR_MID],
- [:TkYIELD, TkKW, "yield", EXPR_END],
- [:TkSUPER, TkKW, "super", EXPR_END],
- [:TkSELF, TkKW, "self", EXPR_END],
- [:TkNIL, TkKW, "nil", EXPR_END],
- [:TkTRUE, TkKW, "true", EXPR_END],
- [:TkFALSE, TkKW, "false", EXPR_END],
- [:TkAND, TkKW, "and", EXPR_BEG],
- [:TkOR, TkKW, "or", EXPR_BEG],
- [:TkNOT, TkKW, "not", EXPR_BEG],
- [:TkIF_MOD, TkKW],
- [:TkUNLESS_MOD, TkKW],
- [:TkWHILE_MOD, TkKW],
- [:TkUNTIL_MOD, TkKW],
- [:TkALIAS, TkKW, "alias", EXPR_FNAME],
- [:TkDEFINED, TkKW, "defined?", EXPR_END],
- [:TklBEGIN, TkKW, "BEGIN", EXPR_END],
- [:TklEND, TkKW, "END", EXPR_END],
- [:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
- [:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
-
- [:TkIDENTIFIER, TkId],
- [:TkFID, TkId],
- [:TkGVAR, TkId],
- [:TkIVAR, TkId],
- [:TkCONSTANT, TkId],
-
- [:TkINTEGER, TkVal],
- [:TkFLOAT, TkVal],
- [:TkSTRING, TkVal],
- [:TkXSTRING, TkVal],
- [:TkREGEXP, TkVal],
- [:TkCOMMENT, TkVal],
-
- [:TkDSTRING, TkNode],
- [:TkDXSTRING, TkNode],
- [:TkDREGEXP, TkNode],
- [:TkNTH_REF, TkId],
- [:TkBACK_REF, TkId],
-
- [:TkUPLUS, TkOp, "+@"],
- [:TkUMINUS, TkOp, "-@"],
- [:TkPOW, TkOp, "**"],
- [:TkCMP, TkOp, "<=>"],
- [:TkEQ, TkOp, "=="],
- [:TkEQQ, TkOp, "==="],
- [:TkNEQ, TkOp, "!="],
- [:TkGEQ, TkOp, ">="],
- [:TkLEQ, TkOp, "<="],
- [:TkANDOP, TkOp, "&&"],
- [:TkOROP, TkOp, "||"],
- [:TkMATCH, TkOp, "=~"],
- [:TkNMATCH, TkOp, "!~"],
- [:TkDOT2, TkOp, ".."],
- [:TkDOT3, TkOp, "..."],
- [:TkAREF, TkOp, "[]"],
- [:TkASET, TkOp, "[]="],
- [:TkLSHFT, TkOp, "<<"],
- [:TkRSHFT, TkOp, ">>"],
- [:TkCOLON2, TkOp],
- [:TkCOLON3, TkOp],
-# [:OPASGN, TkOp], # +=, -= etc. #
- [:TkASSOC, TkOp, "=>"],
- [:TkQUESTION, TkOp, "?"], #?
- [:TkCOLON, TkOp, ":"], #:
-
- [:TkfLPAREN], # func( #
- [:TkfLBRACK], # func[ #
- [:TkfLBRACE], # func{ #
- [:TkSTAR], # *arg
- [:TkAMPER], # &arg #
- [:TkSYMBOL, TkId], # :SYMBOL
- [:TkSYMBEG, TkId],
- [:TkGT, TkOp, ">"],
- [:TkLT, TkOp, "<"],
- [:TkPLUS, TkOp, "+"],
- [:TkMINUS, TkOp, "-"],
- [:TkMULT, TkOp, "*"],
- [:TkDIV, TkOp, "/"],
- [:TkMOD, TkOp, "%"],
- [:TkBITOR, TkOp, "|"],
- [:TkBITXOR, TkOp, "^"],
- [:TkBITAND, TkOp, "&"],
- [:TkBITNOT, TkOp, "~"],
- [:TkNOTOP, TkOp, "!"],
-
- [:TkBACKQUOTE, TkOp, "`"],
-
- [:TkASSIGN, Token, "="],
- [:TkDOT, Token, "."],
- [:TkLPAREN, Token, "("], #(exp)
- [:TkLBRACK, Token, "["], #[arry]
- [:TkLBRACE, Token, "{"], #{hash}
- [:TkRPAREN, Token, ")"],
- [:TkRBRACK, Token, "]"],
- [:TkRBRACE, Token, "}"],
- [:TkCOMMA, Token, ","],
- [:TkSEMICOLON, Token, ";"],
-
- [:TkRD_COMMENT],
- [:TkSPACE],
- [:TkNL],
- [:TkEND_OF_SCRIPT],
-
- [:TkBACKSLASH, TkUnknownChar, "\\"],
- [:TkAT, TkUnknownChar, "@"],
- [:TkDOLLAR, TkUnknownChar, "\$"], #"
- ]
-
- # {reading => token_class}
- # {reading => [token_class, *opt]}
- TkReading2Token = {}
- TkSymbol2Token = {}
-
- def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts)
- token_n = token_n.id2name unless token_n.kind_of?(String)
- if RubyToken.const_defined?(token_n)
- IRB.fail AlreadyDefinedToken, token_n
- end
-
- token_c = Class.new super_token
- RubyToken.const_set token_n, token_c
-# token_c.inspect
-
- if reading
- if TkReading2Token[reading]
- IRB.fail TkReading2TokenDuplicateError, token_n, reading
- end
- if opts.empty?
- TkReading2Token[reading] = [token_c]
- else
- TkReading2Token[reading] = [token_c].concat(opts)
- end
- end
- TkSymbol2Token[token_n.intern] = token_c
-
- if token_c <= TkOp
- token_c.class_eval %{
- def self.op_name; "#{reading}"; end
- }
- end
- end
-
- for defs in TokenDefinitions
- def_token(*defs)
- end
-
- NEWLINE_TOKEN = TkNL.new(0,0)
- NEWLINE_TOKEN.set_text("\n")
-
-end
-
-
-
-# Lexical analyzer for Ruby source
-
-class RubyLex
-
- ######################################################################
- #
- # Read an input stream character by character. We allow for unlimited
- # ungetting of characters just read.
- #
- # We simplify the implementation greatly by reading the entire input
- # into a buffer initially, and then simply traversing it using
- # pointers.
- #
- # We also have to allow for the <i>here document diversion</i>. This
- # little gem comes about when the lexer encounters a here
- # document. At this point we effectively need to split the input
- # stream into two parts: one to read the body of the here document,
- # the other to read the rest of the input line where the here
- # document was initially encountered. For example, we might have
- #
- # do_something(<<-A, <<-B)
- # stuff
- # for
- # A
- # stuff
- # for
- # B
- #
- # When the lexer encounters the <<A, it reads until the end of the
- # line, and keeps it around for later. It then reads the body of the
- # here document. Once complete, it needs to read the rest of the
- # original line, but then skip the here document body.
- #
-
- class BufferedReader
-
- attr_reader :line_num
-
- def initialize(content)
- if /\t/ =~ content
- tab_width = Options.instance.tab_width
- content = content.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
- line
- end .join("\n")
- end
- @content = content
- @content << "\n" unless @content[-1,1] == "\n"
- @size = @content.size
- @offset = 0
- @hwm = 0
- @line_num = 1
- @read_back_offset = 0
- @last_newline = 0
- @newline_pending = false
- end
-
- def column
- @offset - @last_newline
- end
-
- def getc
- return nil if @offset >= @size
- ch = @content[@offset, 1]
-
- @offset += 1
- @hwm = @offset if @hwm < @offset
-
- if @newline_pending
- @line_num += 1
- @last_newline = @offset - 1
- @newline_pending = false
- end
-
- if ch == "\n"
- @newline_pending = true
- end
- ch
- end
-
- def getc_already_read
- getc
- end
-
- def ungetc(ch)
- raise "unget past beginning of file" if @offset <= 0
- @offset -= 1
- if @content[@offset] == ?\n
- @newline_pending = false
- end
- end
-
- def get_read
- res = @content[@read_back_offset...@offset]
- @read_back_offset = @offset
- res
- end
-
- def peek(at)
- pos = @offset + at
- if pos >= @size
- nil
- else
- @content[pos, 1]
- end
- end
-
- def peek_equal(str)
- @content[@offset, str.length] == str
- end
-
- def divert_read_from(reserve)
- @content[@offset, 0] = reserve
- @size = @content.size
- end
- end
-
- # end of nested class BufferedReader
-
- extend Exception2MessageMapper
- def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
- def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkReading2TokenDuplicateError,
- "key duplicate(token_n='%s', key='%s')")
- def_exception(:SyntaxError, "%s")
-
- include RubyToken
- include IRB
-
- attr_reader :continue
- attr_reader :lex_state
-
- def RubyLex.debug?
- false
- end
-
- def initialize(content)
- lex_init
-
- @reader = BufferedReader.new(content)
-
- @exp_line_no = @line_no = 1
- @base_char_no = 0
- @indent = 0
-
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_BEG
- @space_seen = false
-
- @continue = false
- @line = ""
-
- @skip_space = false
- @read_auto_clean_up = false
- @exception_on_syntax_error = true
- end
-
- attr :skip_space, true
- attr :read_auto_clean_up, true
- attr :exception_on_syntax_error, true
-
- attr :indent
-
- # io functions
- def line_no
- @reader.line_num
- end
-
- def char_no
- @reader.column
- end
-
- def get_read
- @reader.get_read
- end
-
- def getc
- @reader.getc
- end
-
- def getc_of_rests
- @reader.getc_already_read
- end
-
- def gets
- c = getc or return
- l = ""
- begin
- l.concat c unless c == "\r"
- break if c == "\n"
- end while c = getc
- l
- end
-
-
- def ungetc(c = nil)
- @reader.ungetc(c)
- end
-
- def peek_equal?(str)
- @reader.peek_equal(str)
- end
-
- def peek(i = 0)
- @reader.peek(i)
- end
-
- def lex
- until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
- !@continue or
- tk.nil?)
- end
- line = get_read
-
- if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
- nil
- else
- line
- end
- end
-
- def token
- set_token_position(line_no, char_no)
- begin
- begin
- tk = @OP.match(self)
- @space_seen = tk.kind_of?(TkSPACE)
- rescue SyntaxError
- abort if @exception_on_syntax_error
- tk = TkError.new(line_no, char_no)
- end
- end while @skip_space and tk.kind_of?(TkSPACE)
- if @read_auto_clean_up
- get_read
- end
-# throw :eof unless tk
- p tk if $DEBUG
- tk
- end
-
- ENINDENT_CLAUSE = [
- "case", "class", "def", "do", "for", "if",
- "module", "unless", "until", "while", "begin" #, "when"
- ]
- DEINDENT_CLAUSE = ["end" #, "when"
- ]
-
- PERCENT_LTYPE = {
- "q" => "\'",
- "Q" => "\"",
- "x" => "\`",
- "r" => "/",
- "w" => "]"
- }
-
- PERCENT_PAREN = {
- "{" => "}",
- "[" => "]",
- "<" => ">",
- "(" => ")"
- }
-
- Ltype2Token = {
- "\'" => TkSTRING,
- "\"" => TkSTRING,
- "\`" => TkXSTRING,
- "/" => TkREGEXP,
- "]" => TkDSTRING
- }
- Ltype2Token.default = TkSTRING
-
- DLtype2Token = {
- "\"" => TkDSTRING,
- "\`" => TkDXSTRING,
- "/" => TkDREGEXP,
- }
-
- def lex_init()
- @OP = SLex.new
- @OP.def_rules("\0", "\004", "\032") do |chars, io|
- Token(TkEND_OF_SCRIPT).set_text(chars)
- end
-
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io|
- @space_seen = TRUE
- while (ch = getc) =~ /[ \t\f\r\13]/
- chars << ch
- end
- ungetc
- Token(TkSPACE).set_text(chars)
- end
-
- @OP.def_rule("#") do
- |op, io|
- identify_comment
- end
-
- @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do
- |op, io|
- str = op
- @ltype = "="
-
-
- begin
- line = ""
- begin
- ch = getc
- line << ch
- end until ch == "\n"
- str << line
- end until line =~ /^=end/
-
- ungetc
-
- @ltype = nil
-
- if str =~ /\A=begin\s+rdoc/i
- str.sub!(/\A=begin.*\n/, '')
- str.sub!(/^=end.*/m, '')
- Token(TkCOMMENT).set_text(str)
- else
- Token(TkRD_COMMENT)#.set_text(str)
- end
- end
-
- @OP.def_rule("\n") do
- print "\\n\n" if RubyLex.debug?
- case @lex_state
- when EXPR_BEG, EXPR_FNAME, EXPR_DOT
- @continue = TRUE
- else
- @continue = FALSE
- @lex_state = EXPR_BEG
- end
- Token(TkNL).set_text("\n")
- end
-
- @OP.def_rules("*", "**",
- "!", "!=", "!~",
- "=", "==", "===",
- "=~", "<=>",
- "<", "<=",
- ">", ">=", ">>") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rules("<<") do
- |op, io|
- tk = nil
- if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
- (@lex_state != EXPR_ARG || @space_seen)
- c = peek(0)
- if /[-\w_\"\'\`]/ =~ c
- tk = identify_here_document
- end
- end
- if !tk
- @lex_state = EXPR_BEG
- tk = Token(op).set_text(op)
- end
- tk
- end
-
- @OP.def_rules("'", '"') do
- |op, io|
- identify_string(op)
- end
-
- @OP.def_rules("`") do
- |op, io|
- if @lex_state == EXPR_FNAME
- Token(op).set_text(op)
- else
- identify_string(op)
- end
- end
-
- @OP.def_rules('?') do
- |op, io|
- if @lex_state == EXPR_END
- @lex_state = EXPR_BEG
- Token(TkQUESTION).set_text(op)
- else
- ch = getc
- if @lex_state == EXPR_ARG && ch !~ /\s/
- ungetc
- @lex_state = EXPR_BEG;
- Token(TkQUESTION).set_text(op)
- else
- str = op
- str << ch
- if (ch == '\\') #'
- str << read_escape
- end
- @lex_state = EXPR_END
- Token(TkINTEGER).set_text(str)
- end
- end
- end
-
- @OP.def_rules("&", "&&", "|", "||") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rules("+=", "-=", "*=", "**=",
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
- |op, io|
- @lex_state = EXPR_BEG
- op =~ /^(.*)=$/
- Token(TkOPASGN, $1).set_text(op)
- end
-
- @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io|
- Token(TkUPLUS).set_text(op)
- end
-
- @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io|
- Token(TkUMINUS).set_text(op)
- end
-
- @OP.def_rules("+", "-") do
- |op, io|
- catch(:RET) do
- if @lex_state == EXPR_ARG
- if @space_seen and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- Token(op).set_text(op)
- end
- end
-
- @OP.def_rule(".") do
- @lex_state = EXPR_BEG
- if peek(0) =~ /[0-9]/
- ungetc
- identify_number("")
- else
- # for obj.if
- @lex_state = EXPR_DOT
- Token(TkDOT).set_text(".")
- end
- end
-
- @OP.def_rules("..", "...") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- lex_int2
- end
-
- def lex_int2
- @OP.def_rules("]", "}", ")") do
- |op, io|
- @lex_state = EXPR_END
- @indent -= 1
- Token(op).set_text(op)
- end
-
- @OP.def_rule(":") do
- if @lex_state == EXPR_END || peek(0) =~ /\s/
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON)
- else
- @lex_state = EXPR_FNAME;
- tk = Token(TkSYMBEG)
- end
- tk.set_text(":")
- end
-
- @OP.def_rule("::") do
-# p @lex_state.id2name, @space_seen
- if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON3)
- else
- @lex_state = EXPR_DOT
- tk = Token(TkCOLON2)
- end
- tk.set_text("::")
- end
-
- @OP.def_rule("/") do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_string(op)
- elsif peek(0) == '='
- getc
- @lex_state = EXPR_BEG
- Token(TkOPASGN, :/).set_text("/=") #")
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_string(op)
- else
- @lex_state = EXPR_BEG
- Token("/").set_text(op)
- end
- end
-
- @OP.def_rules("^") do
- @lex_state = EXPR_BEG
- Token("^").set_text("^")
- end
-
- # @OP.def_rules("^=") do
- # @lex_state = EXPR_BEG
- # Token(TkOPASGN, :^)
- # end
-
- @OP.def_rules(",", ";") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rule("~") do
- @lex_state = EXPR_BEG
- Token("~").set_text("~")
- end
-
- @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do
- @lex_state = EXPR_BEG
- Token("~").set_text("~@")
- end
-
- @OP.def_rule("(") do
- @indent += 1
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- @lex_state = EXPR_BEG
- tk = Token(TkfLPAREN)
- else
- @lex_state = EXPR_BEG
- tk = Token(TkLPAREN)
- end
- tk.set_text("(")
- end
-
- @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do
- Token("[]").set_text("[]")
- end
-
- @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do
- Token("[]=").set_text("[]=")
- end
-
- @OP.def_rule("[") do
- @indent += 1
- if @lex_state == EXPR_FNAME
- t = Token(TkfLBRACK)
- else
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- t = Token(TkLBRACK)
- elsif @lex_state == EXPR_ARG && @space_seen
- t = Token(TkLBRACK)
- else
- t = Token(TkfLBRACK)
- end
- @lex_state = EXPR_BEG
- end
- t.set_text("[")
- end
-
- @OP.def_rule("{") do
- @indent += 1
- if @lex_state != EXPR_END && @lex_state != EXPR_ARG
- t = Token(TkLBRACE)
- else
- t = Token(TkfLBRACE)
- end
- @lex_state = EXPR_BEG
- t.set_text("{")
- end
-
- @OP.def_rule('\\') do #'
- if getc == "\n"
- @space_seen = true
- @continue = true
- Token(TkSPACE).set_text("\\\n")
- else
- ungetc
- Token("\\").set_text("\\") #"
- end
- end
-
- @OP.def_rule('%') do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_quotation('%')
- elsif peek(0) == '='
- getc
- Token(TkOPASGN, "%").set_text("%=")
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_quotation('%')
- else
- @lex_state = EXPR_BEG
- Token("%").set_text("%")
- end
- end
-
- @OP.def_rule('$') do #'
- identify_gvar
- end
-
- @OP.def_rule('@') do
- if peek(0) =~ /[@\w_]/
- ungetc
- identify_identifier
- else
- Token("@").set_text("@")
- end
- end
-
- # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do
- # |op, io|
- # @indent += 1
- # @lex_state = EXPR_FNAME
- # # @lex_state = EXPR_END
- # # until @rests[0] == "\n" or @rests[0] == ";"
- # # rests.shift
- # # end
- # end
-
- @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
- throw :eof
- end
-
- @OP.def_rule("") do
- |op, io|
- printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
- if peek(0) =~ /[0-9]/
- t = identify_number("")
- elsif peek(0) =~ /[\w_]/
- t = identify_identifier
- end
- printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
- t
- end
-
- p @OP if RubyLex.debug?
- end
-
- def identify_gvar
- @lex_state = EXPR_END
- str = "$"
-
- tk = case ch = getc
- when /[~_*$?!@\/\\;,=:<>".]/ #"
- str << ch
- Token(TkGVAR, str)
-
- when "-"
- str << "-" << getc
- Token(TkGVAR, str)
-
- when "&", "`", "'", "+"
- str << ch
- Token(TkBACK_REF, str)
-
- when /[1-9]/
- str << ch
- while (ch = getc) =~ /[0-9]/
- str << ch
- end
- ungetc
- Token(TkNTH_REF)
- when /\w/
- ungetc
- ungetc
- return identify_identifier
- else
- ungetc
- Token("$")
- end
- tk.set_text(str)
- end
-
- def identify_identifier
- token = ""
- token.concat getc if peek(0) =~ /[$@]/
- token.concat getc if peek(0) == "@"
-
- while (ch = getc) =~ /\w|_/
- print ":", ch, ":" if RubyLex.debug?
- token.concat ch
- end
- ungetc
-
- if ch == "!" or ch == "?"
- token.concat getc
- end
- # fix token
-
- # $stderr.puts "identifier - #{token}, state = #@lex_state"
-
- case token
- when /^\$/
- return Token(TkGVAR, token).set_text(token)
- when /^\@/
- @lex_state = EXPR_END
- return Token(TkIVAR, token).set_text(token)
- end
-
- if @lex_state != EXPR_DOT
- print token, "\n" if RubyLex.debug?
-
- token_c, *trans = TkReading2Token[token]
- if token_c
- # reserved word?
-
- if (@lex_state != EXPR_BEG &&
- @lex_state != EXPR_FNAME &&
- trans[1])
- # modifiers
- token_c = TkSymbol2Token[trans[1]]
- @lex_state = trans[0]
- else
- if @lex_state != EXPR_FNAME
- if ENINDENT_CLAUSE.include?(token)
- @indent += 1
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- end
- @lex_state = trans[0]
- else
- @lex_state = EXPR_END
- end
- end
- return Token(token_c, token).set_text(token)
- end
- end
-
- if @lex_state == EXPR_FNAME
- @lex_state = EXPR_END
- if peek(0) == '='
- token.concat getc
- end
- elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_END
- end
-
- if token[0, 1] =~ /[A-Z]/
- return Token(TkCONSTANT, token).set_text(token)
- elsif token[token.size - 1, 1] =~ /[!?]/
- return Token(TkFID, token).set_text(token)
- else
- return Token(TkIDENTIFIER, token).set_text(token)
- end
- end
-
- def identify_here_document
- ch = getc
- if ch == "-"
- ch = getc
- indent = true
- end
- if /['"`]/ =~ ch # '
- lt = ch
- quoted = ""
- while (c = getc) && c != lt
- quoted.concat c
- end
- else
- lt = '"'
- quoted = ch.dup
- while (c = getc) && c =~ /\w/
- quoted.concat c
- end
- ungetc
- end
-
- ltback, @ltype = @ltype, lt
- reserve = ""
-
- while ch = getc
- reserve << ch
- if ch == "\\" #"
- ch = getc
- reserve << ch
- elsif ch == "\n"
- break
- end
- end
-
- str = ""
- while (l = gets)
- l.chomp!
- l.strip! if indent
- break if l == quoted
- str << l.chomp << "\n"
- end
-
- @reader.divert_read_from(reserve)
-
- @ltype = ltback
- @lex_state = EXPR_END
- Token(Ltype2Token[lt], str).set_text(str.dump)
- end
-
- def identify_quotation(initial_char)
- ch = getc
- if lt = PERCENT_LTYPE[ch]
- initial_char += ch
- ch = getc
- elsif ch =~ /\W/
- lt = "\""
- else
- RubyLex.fail SyntaxError, "unknown type of %string ('#{ch}')"
- end
-# if ch !~ /\W/
-# ungetc
-# next
-# end
- #@ltype = lt
- @quoted = ch unless @quoted = PERCENT_PAREN[ch]
- identify_string(lt, @quoted, ch, initial_char)
- end
-
- def identify_number(start)
- str = start.dup
-
- if start == "+" or start == "-" or start == ""
- start = getc
- str << start
- end
-
- @lex_state = EXPR_END
-
- if start == "0"
- if peek(0) == "x"
- ch = getc
- str << ch
- match = /[0-9a-f_]/
- else
- match = /[0-7_]/
- end
- while ch = getc
- if ch !~ match
- ungetc
- break
- else
- str << ch
- end
- end
- return Token(TkINTEGER).set_text(str)
- end
-
- type = TkINTEGER
- allow_point = TRUE
- allow_e = TRUE
- while ch = getc
- case ch
- when /[0-9_]/
- str << ch
-
- when allow_point && "."
- type = TkFLOAT
- if peek(0) !~ /[0-9]/
- ungetc
- break
- end
- str << ch
- allow_point = false
-
- when allow_e && "e", allow_e && "E"
- str << ch
- type = TkFLOAT
- if peek(0) =~ /[+-]/
- str << getc
- end
- allow_e = false
- allow_point = false
- else
- ungetc
- break
- end
- end
- Token(type).set_text(str)
- end
-
- def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil)
- @ltype = ltype
- @quoted = quoted
- subtype = nil
-
- str = ""
- str << initial_char if initial_char
- str << (opener||quoted)
-
- nest = 0
- begin
- while ch = getc
- str << ch
- if @quoted == ch
- if nest == 0
- break
- else
- nest -= 1
- end
- elsif opener == ch
- nest += 1
- elsif @ltype != "'" && @ltype != "]" and ch == "#"
- ch = getc
- if ch == "{"
- subtype = true
- str << ch << skip_inner_expression
- else
- ungetc(ch)
- end
- elsif ch == '\\' #'
- str << read_escape
- end
- end
- if @ltype == "/"
- if peek(0) =~ /i|o|n|e|s/
- str << getc
- end
- end
- if subtype
- Token(DLtype2Token[ltype], str)
- else
- Token(Ltype2Token[ltype], str)
- end.set_text(str)
- ensure
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_END
- end
- end
-
- def skip_inner_expression
- res = ""
- nest = 0
- while (ch = getc)
- res << ch
- if ch == '}'
- break if nest.zero?
- nest -= 1
- elsif ch == '{'
- nest += 1
- end
- end
- res
- end
-
- def identify_comment
- @ltype = "#"
- comment = "#"
- while ch = getc
- if ch == "\\"
- ch = getc
- if ch == "\n"
- ch = " "
- else
- comment << "\\"
- end
- else
- if ch == "\n"
- @ltype = nil
- ungetc
- break
- end
- end
- comment << ch
- end
- return Token(TkCOMMENT).set_text(comment)
- end
-
- def read_escape
- res = ""
- case ch = getc
- when /[0-7]/
- ungetc ch
- 3.times do
- case ch = getc
- when /[0-7]/
- when nil
- break
- else
- ungetc
- break
- end
- res << ch
- end
-
- when "x"
- res << ch
- 2.times do
- case ch = getc
- when /[0-9a-fA-F]/
- when nil
- break
- else
- ungetc
- break
- end
- res << ch
- end
-
- when "M"
- res << ch
- if (ch = getc) != '-'
- ungetc
- else
- res << ch
- if (ch = getc) == "\\" #"
- res << ch
- res << read_escape
- else
- res << ch
- end
- end
-
- when "C", "c" #, "^"
- res << ch
- if ch == "C" and (ch = getc) != "-"
- ungetc
- else
- res << ch
- if (ch = getc) == "\\" #"
- res << ch
- res << read_escape
- else
- res << ch
- end
- end
- else
- res << ch
- end
- res
- end
-end
-
-
-
-# Extract code elements from a source file, returning a TopLevel
-# object containing the constituent file elements.
-#
-# This file is based on rtags
-
-module RDoc
-
- GENERAL_MODIFIERS = [ 'nodoc' ].freeze
-
- CLASS_MODIFIERS = GENERAL_MODIFIERS
-
- ATTR_MODIFIERS = GENERAL_MODIFIERS
-
- CONSTANT_MODIFIERS = GENERAL_MODIFIERS
-
- METHOD_MODIFIERS = GENERAL_MODIFIERS +
- [ 'arg', 'args', 'yield', 'yields', 'notnew', 'not-new', 'not_new', 'doc' ]
-
-
- class RubyParser
- include RubyToken
- include TokenStream
-
- extend ParserFactory
-
- parse_files_matching(/\.rbw?$/)
-
-
- def initialize(top_level, file_name, content, options, stats)
- @options = options
- @stats = stats
- @size = 0
- @token_listeners = nil
- @input_file_name = file_name
- @scanner = RubyLex.new(content)
- @scanner.exception_on_syntax_error = false
- @top_level = top_level
- @progress = $stderr unless options.quiet
- end
-
- def scan
- @tokens = []
- @unget_read = []
- @read = []
- catch(:eof) do
- catch(:enddoc) do
- begin
- parse_toplevel_statements(@top_level)
- rescue Exception => e
- $stderr.puts "\n\n"
- $stderr.puts "RDoc failure in #@input_file_name at or around " +
- "line #{@scanner.line_no} column #{@scanner.char_no}"
- $stderr.puts
- $stderr.puts "Before reporting this, could you check that the file"
- $stderr.puts "you're documenting compiles cleanly--RDoc is not a"
- $stderr.puts "full Ruby parser, and gets confused easily if fed"
- $stderr.puts "invalid programs."
- $stderr.puts
- $stderr.puts "The internal error was:\n\n"
-
- e.set_backtrace(e.backtrace[0,4])
- raise
- end
- end
- end
- @top_level
- end
-
- private
-
- def make_message(msg)
- prefix = "\n" + @input_file_name + ":"
- if @scanner
- prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
- end
- return prefix + msg
- end
-
- def warn(msg)
- return if @options.quiet
- msg = make_message msg
- $stderr.puts msg
- end
-
- def error(msg)
- msg = make_message msg
- $stderr.puts msg
- exit(1)
- end
-
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
- end
- end
-
- def add_token_listener(obj)
- @token_listeners ||= []
- @token_listeners << obj
- end
-
- def remove_token_listener(obj)
- @token_listeners.delete(obj)
- end
-
- def get_tk
- tk = nil
- if @tokens.empty?
- tk = @scanner.token
- @read.push @scanner.get_read
- puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
- else
- @read.push @unget_read.shift
- tk = @tokens.shift
- puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
- end
-
- if tk.kind_of?(TkSYMBEG)
- set_token_position(tk.line_no, tk.char_no)
- tk1 = get_tk
- if tk1.kind_of?(TkId) || tk1.kind_of?(TkOp)
- tk = Token(TkSYMBOL).set_text(":" + tk1.name)
- # remove the identifier we just read (we're about to
- # replace it with a symbol)
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- else
- warn("':' not followed by identified or operator")
- tk = tk1
- end
- end
-
- # inform any listeners of our shiny new token
- @token_listeners.each do |obj|
- obj.add_token(tk)
- end if @token_listeners
-
- tk
- end
-
- def peek_tk
- unget_tk(tk = get_tk)
- tk
- end
-
- def unget_tk(tk)
- @tokens.unshift tk
- @unget_read.unshift @read.pop
-
- # Remove this token from any listeners
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- end
-
- def skip_tkspace(skip_nl = true)
- tokens = []
- while ((tk = get_tk).kind_of?(TkSPACE) ||
- (skip_nl && tk.kind_of?(TkNL)))
- tokens.push tk
- end
- unget_tk(tk)
- tokens
- end
-
- def get_tkread
- read = @read.join("")
- @read = []
- read
- end
-
- def peek_read
- @read.join('')
- end
-
- NORMAL = "::"
- SINGLE = "<<"
-
- # Look for the first comment in a file that isn't
- # a shebang line.
-
- def collect_first_comment
- skip_tkspace
- res = ''
- first_line = true
-
- tk = get_tk
- while tk.kind_of?(TkCOMMENT)
- if first_line && tk.text[0,2] == "#!"
- skip_tkspace
- tk = get_tk
- else
- res << tk.text << "\n"
- tk = get_tk
- if tk.kind_of? TkNL
- skip_tkspace(false)
- tk = get_tk
- end
- end
- first_line = false
- end
- unget_tk(tk)
- res
- end
-
- def parse_toplevel_statements(container)
- comment = collect_first_comment
- look_for_directives_in(container, comment)
- container.comment = comment unless comment.empty?
- parse_statements(container, NORMAL, nil, comment)
- end
-
- def parse_statements(container, single=NORMAL, current_method=nil, comment='')
- nest = 1
- save_visibility = container.visibility
-
-# if container.kind_of?(TopLevel)
-# else
-# comment = ''
-# end
-
- non_comment_seen = true
-
- while tk = get_tk
-
- keep_comment = false
-
- non_comment_seen = true unless tk.kind_of?(TkCOMMENT)
-
- case tk
-
- when TkNL
- skip_tkspace(true) # Skip blanks and newlines
- tk = get_tk
- if tk.kind_of?(TkCOMMENT)
- if non_comment_seen
- comment = ''
- non_comment_seen = false
- end
- while tk.kind_of?(TkCOMMENT)
- comment << tk.text << "\n"
- tk = get_tk # this is the newline
- skip_tkspace(false) # leading spaces
- tk = get_tk
- end
- unless comment.empty?
- look_for_directives_in(container, comment)
- if container.done_documenting
- container.ongoing_visibility = save_visibility
-# return
- end
- end
- keep_comment = true
- else
- non_comment_seen = true
- end
- unget_tk(tk)
- keep_comment = true
-
-
- when TkCLASS
- if container.document_children
- parse_class(container, single, tk, comment)
- else
- nest += 1
- end
-
- when TkMODULE
- if container.document_children
- parse_module(container, single, tk, comment)
- else
- nest += 1
- end
-
- when TkDEF
- if container.document_self
- parse_method(container, single, tk, comment)
- else
- nest += 1
- end
-
- when TkCONSTANT
- if container.document_self
- parse_constant(container, single, tk, comment)
- end
-
- when TkALIAS
- if container.document_self
- parse_alias(container, single, tk, comment)
- end
-
- when TkYIELD
- if current_method.nil?
- warn("Warning: yield outside of method") if container.document_self
- else
- parse_yield(container, single, tk, current_method)
- end
-
- # Until and While can have a 'do', which shouldn't increas
- # the nesting. We can't solve the general case, but we can
- # handle most occurrences by ignoring a do at the end of a line
-
- when TkUNTIL, TkWHILE
- nest += 1
- puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG
- skip_optional_do_after_expression
-
- # 'for' is trickier
- when TkFOR
- nest += 1
- puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG
- skip_for_variable
- skip_optional_do_after_expression
-
- when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN
- nest += 1
- puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG
-
- when TkIDENTIFIER
- if nest == 1 and current_method.nil?
- case tk.name
- when "private", "protected", "public",
- "private_class_method", "public_class_method"
- parse_visibility(container, single, tk)
- keep_comment = true
- when "attr"
- parse_attr(container, single, tk, comment)
- when /^attr_(reader|writer|accessor)$/, @options.extra_accessors
- parse_attr_accessor(container, single, tk, comment)
- when "alias_method"
- if container.document_self
- parse_alias(container, single, tk, comment)
- end
- end
- end
-
- case tk.name
- when "require"
- parse_require(container, comment)
- when "include"
- parse_include(container, comment)
- end
-
-
- when TkEND
- nest -= 1
- puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG
- puts "Method = #{current_method.name}" if $DEBUG and current_method
- if nest == 0
- read_documentation_modifiers(container, CLASS_MODIFIERS)
- container.ongoing_visibility = save_visibility
- return
- end
-
- end
-
- comment = '' unless keep_comment
- begin
- get_tkread
- skip_tkspace(false)
- end while peek_tk == TkNL
-
- end
- end
-
- def parse_class(container, single, tk, comment, &block)
- progress("c")
-
- @stats.num_classes += 1
-
- container, name_t = get_class_or_module(container)
-
- case name_t
- when TkCONSTANT
- name = name_t.name
- superclass = "Object"
-
- if peek_tk.kind_of?(TkLT)
- get_tk
- skip_tkspace(true)
- superclass = get_class_specification
- superclass = "<unknown>" if superclass.empty?
- end
-
- if single == SINGLE
- cls_type = SingleClass
- else
- cls_type = NormalClass
- end
-
- cls = container.add_class(cls_type, name, superclass)
- read_documentation_modifiers(cls, CLASS_MODIFIERS)
- cls.record_location(@top_level)
- parse_statements(cls)
- cls.comment = comment
-
- when TkLSHFT
- case name = get_class_specification
- when "self", container.name
- parse_statements(container, SINGLE, &block)
- else
- other = TopLevel.find_class_named(name)
- unless other
-# other = @top_level.add_class(NormalClass, name, nil)
-# other.record_location(@top_level)
-# other.comment = comment
- other = NormalClass.new("Dummy", nil)
- end
- read_documentation_modifiers(other, CLASS_MODIFIERS)
- parse_statements(other, SINGLE, &block)
- end
-
- else
- warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
- end
- end
-
- def parse_module(container, single, tk, comment)
- progress("m")
- @stats.num_modules += 1
- container, name_t = get_class_or_module(container)
-# skip_tkspace
- name = name_t.name
- mod = container.add_module(NormalModule, name)
- mod.record_location(@top_level)
- read_documentation_modifiers(mod, CLASS_MODIFIERS)
- parse_statements(mod)
- mod.comment = comment
- end
-
- # Look for the name of a class of module (optionally with a leading :: or
- # with :: separated named) and return the ultimate name and container
-
- def get_class_or_module(container)
- skip_tkspace
- name_t = get_tk
-
- # class ::A -> A is in the top level
- if name_t.kind_of?(TkCOLON2)
- name_t = get_tk
- container = @top_level
- end
-
- skip_tkspace(false)
-
- while peek_tk.kind_of?(TkCOLON2)
- prev_container = container
- container = container.find_module_named(name_t.name)
- if !container
-# warn("Couldn't find module #{name_t.name}")
- container = prev_container.add_module(NormalModule, name_t.name)
- end
- get_tk
- name_t = get_tk
- end
- skip_tkspace(false)
- return [container, name_t]
- end
-
- def parse_constant(container, single, tk, comment)
- name = tk.name
- skip_tkspace(false)
- eq_tk = get_tk
-
- unless eq_tk.kind_of?(TkASSIGN)
- unget_tk(eq_tk)
- return
- end
-
-
- nest = 0
- get_tkread
-
- tk = get_tk
- if tk.kind_of? TkGT
- unget_tk(tk)
- unget_tk(eq_tk)
- return
- end
-
- loop do
- puts("Param: #{tk}, #{@scanner.continue} " +
- "#{@scanner.lex_state} #{nest}") if $DEBUG
-
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when TkRPAREN
- nest -= 1
- when TkCOMMENT
- if nest <= 0 && @scanner.lex_state == EXPR_END
- unget_tk(tk)
- break
- end
- when TkNL
- if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue
- unget_tk(tk)
- break
- end
- end
- tk = get_tk
- end
-
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- con = Constant.new(name, res, comment)
- read_documentation_modifiers(con, CONSTANT_MODIFIERS)
- if con.document_self
- container.add_constant(con)
- end
- end
-
- def parse_method(container, single, tk, comment)
- progress(".")
- @stats.num_methods += 1
- line_no = tk.line_no
- column = tk.char_no
-
- start_collecting_tokens
- add_token(tk)
- add_token_listener(self)
-
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
- skip_tkspace(false)
- name_t = get_tk
- back_tk = skip_tkspace
- meth = nil
- added_container = false
-
- dot = get_tk
- if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2)
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
- skip_tkspace
- name_t2 = get_tk
- case name_t
- when TkSELF
- name = name_t2.name
- when TkCONSTANT
- name = name_t2.name
- prev_container = container
- container = container.find_module_named(name_t.name)
- if !container
- added_container = true
- obj = name_t.name.split("::").inject(Object) do |state, item|
- state.const_get(item)
- end rescue nil
-
- type = obj.class == Class ? NormalClass : NormalModule
- if not [Class, Module].include?(obj.class)
- warn("Couldn't find #{name_t.name}. Assuming it's a module")
- end
-
- if type == NormalClass then
- container = prev_container.add_class(type, name_t.name, obj.superclass.name)
- else
- container = prev_container.add_module(type, name_t.name)
- end
- end
- else
- # warn("Unexpected token '#{name_t2.inspect}'")
- # break
- skip_method(container)
- return
- end
- meth = AnyMethod.new(get_tkread, name)
- meth.singleton = true
- else
- unget_tk dot
- back_tk.reverse_each do
- |tk|
- unget_tk tk
- end
- name = name_t.name
-
- meth = AnyMethod.new(get_tkread, name)
- meth.singleton = (single == SINGLE)
- end
-
- remove_token_listener(self)
-
- meth.start_collecting_tokens
- indent = TkSPACE.new(1,1)
- indent.set_text(" " * column)
-
- meth.add_tokens([TkCOMMENT.new(line_no,
- 1,
- "# File #{@top_level.file_absolute_name}, line #{line_no}"),
- NEWLINE_TOKEN,
- indent])
-
- meth.add_tokens(@token_stream)
-
- add_token_listener(meth)
-
- @scanner.instance_eval{@continue = false}
- parse_method_parameters(meth)
-
- if meth.document_self
- container.add_method(meth)
- elsif added_container
- container.document_self = false
- end
-
- # Having now read the method parameters and documentation modifiers, we
- # now know whether we have to rename #initialize to ::new
-
- if name == "initialize" && !meth.singleton
- if meth.dont_rename_initialize
- meth.visibility = :protected
- else
- meth.singleton = true
- meth.name = "new"
- meth.visibility = :public
- end
- end
-
- parse_statements(container, single, meth)
-
- remove_token_listener(meth)
-
- # Look for a 'call-seq' in the comment, and override the
- # normal parameter stuff
-
- if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '')
- seq = $1
- seq.gsub!(/^\s*\#\s*/, '')
- meth.call_seq = seq
- end
-
- meth.comment = comment
-
- end
-
- def skip_method(container)
- meth = AnyMethod.new("", "anon")
- parse_method_parameters(meth)
- parse_statements(container, false, meth)
- end
-
- # Capture the method's parameters. Along the way,
- # look for a comment containing
- #
- # # yields: ....
- #
- # and add this as the block_params for the method
-
- def parse_method_parameters(method)
- res = parse_method_or_yield_parameters(method)
- res = "(" + res + ")" unless res[0] == ?(
- method.params = res unless method.params
- if method.block_params.nil?
- skip_tkspace(false)
- read_documentation_modifiers(method, METHOD_MODIFIERS)
- end
- end
-
- def parse_method_or_yield_parameters(method=nil, modifiers=METHOD_MODIFIERS)
- skip_tkspace(false)
- tk = get_tk
-
- # Little hack going on here. In the statement
- # f = 2*(1+yield)
- # We see the RPAREN as the next token, so we need
- # to exit early. This still won't catch all cases
- # (such as "a = yield + 1"
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
- return ""
- else
- TkNL
- end
- nest = 0
-
- loop do
- puts("Param: #{tk.inspect}, #{@scanner.continue} " +
- "#{@scanner.lex_state} #{nest}") if $DEBUG
- case tk
- when TkSEMICOLON
- break
- when TkLBRACE
- nest += 1
- when TkRBRACE
- # we might have a.each {|i| yield i }
- unget_tk(tk) if nest.zero?
- nest -= 1
- break if nest <= 0
- when TkLPAREN, TkfLPAREN
- nest += 1
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest <= 0
- else
- break unless @scanner.continue
- end
- when method && method.block_params.nil? && TkCOMMENT
- unget_tk(tk)
- read_documentation_modifiers(method, modifiers)
- end
- tk = get_tk
- end
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- res
- end
-
- # skip the var [in] part of a 'for' statement
- def skip_for_variable
- skip_tkspace(false)
- tk = get_tk
- skip_tkspace(false)
- tk = get_tk
- unget_tk(tk) unless tk.kind_of?(TkIN)
- end
-
- # while, until, and for have an optional
- def skip_optional_do_after_expression
- skip_tkspace(false)
- tk = get_tk
- case tk
- when TkLPAREN, TkfLPAREN
- end_token = TkRPAREN
- else
- end_token = TkNL
- end
-
- nest = 0
- @scanner.instance_eval{@continue = false}
-
- loop do
- puts("\nWhile: #{tk}, #{@scanner.continue} " +
- "#{@scanner.lex_state} #{nest}") if $DEBUG
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when TkDO
- break if nest.zero?
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest.zero?
- else
- break unless @scanner.continue
- end
- end
- tk = get_tk
- end
- skip_tkspace(false)
- if peek_tk.kind_of? TkDO
- get_tk
- end
- end
-
- # Return a superclass, which can be either a constant
- # of an expression
-
- def get_class_specification
- tk = get_tk
- return "self" if tk.kind_of?(TkSELF)
-
- res = ""
- while tk.kind_of?(TkCOLON2) ||
- tk.kind_of?(TkCOLON3) ||
- tk.kind_of?(TkCONSTANT)
-
- res += tk.text
- tk = get_tk
- end
-
- unget_tk(tk)
- skip_tkspace(false)
-
- get_tkread # empty out read buffer
-
- tk = get_tk
-
- case tk
- when TkNL, TkCOMMENT, TkSEMICOLON
- unget_tk(tk)
- return res
- end
-
- res += parse_call_parameters(tk)
- res
- end
-
- def parse_call_parameters(tk)
-
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
- return ""
- else
- TkNL
- end
- nest = 0
-
- loop do
- puts("Call param: #{tk}, #{@scanner.continue} " +
- "#{@scanner.lex_state} #{nest}") if $DEBUG
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest <= 0
- else
- break unless @scanner.continue
- end
- when TkCOMMENT
- unget_tk(tk)
- break
- end
- tk = get_tk
- end
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- res
- end
-
-
- # Parse a constant, which might be qualified by
- # one or more class or module names
-
- def get_constant
- res = ""
- skip_tkspace(false)
- tk = get_tk
-
- while tk.kind_of?(TkCOLON2) ||
- tk.kind_of?(TkCOLON3) ||
- tk.kind_of?(TkCONSTANT)
-
- res += tk.text
- tk = get_tk
- end
-
-# if res.empty?
-# warn("Unexpected token #{tk} in constant")
-# end
- unget_tk(tk)
- res
- end
-
- # Get a constant that may be surrounded by parens
-
- def get_constant_with_optional_parens
- skip_tkspace(false)
- nest = 0
- while (tk = peek_tk).kind_of?(TkLPAREN) || tk.kind_of?(TkfLPAREN)
- get_tk
- skip_tkspace(true)
- nest += 1
- end
-
- name = get_constant
-
- while nest > 0
- skip_tkspace(true)
- tk = get_tk
- nest -= 1 if tk.kind_of?(TkRPAREN)
- end
- name
- end
-
- # Directives are modifier comments that can appear after class, module,
- # or method names. For example
- #
- # def fred # :yields: a, b
- #
- # or
- #
- # class SM # :nodoc:
- #
- # we return the directive name and any parameters as a two element array
-
- def read_directive(allowed)
- tk = get_tk
- puts "directive: #{tk.inspect}" if $DEBUG
- result = nil
- if tk.kind_of?(TkCOMMENT)
- if tk.text =~ /\s*:?(\w+):\s*(.*)/
- directive = $1.downcase
- if allowed.include?(directive)
- result = [directive, $2]
- end
- end
- else
- unget_tk(tk)
- end
- result
- end
-
-
- def read_documentation_modifiers(context, allow)
- dir = read_directive(allow)
-
- case dir[0]
-
- when "notnew", "not_new", "not-new"
- context.dont_rename_initialize = true
-
- when "nodoc"
- context.document_self = false
- if dir[1].downcase == "all"
- context.document_children = false
- end
-
- when "doc"
- context.document_self = true
- context.force_documentation = true
-
- when "yield", "yields"
- unless context.params.nil?
- context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
- end
- context.block_params = dir[1]
-
- when "arg", "args"
- context.params = dir[1]
- end if dir
- end
-
-
- # Look for directives in a normal comment block:
- #
- # #-- - don't display comment from this point forward
- #
- #
- # This routine modifies it's parameter
-
- def look_for_directives_in(context, comment)
-
- preprocess = SM::PreProcess.new(@input_file_name,
- @options.rdoc_include)
-
- preprocess.handle(comment) do |directive, param|
- case directive
- when "stopdoc"
- context.stop_doc
- ""
- when "startdoc"
- context.start_doc
- context.force_documentation = true
- ""
-
- when "enddoc"
- #context.done_documenting = true
- #""
- throw :enddoc
-
- when "main"
- options = Options.instance
- options.main_page = param
- ""
-
- when "title"
- options = Options.instance
- options.title = param
- ""
-
- when "section"
- context.set_current_section(param, comment)
- comment.replace("") # 1.8 doesn't support #clear
- break
- else
- warn "Unrecognized directive '#{directive}'"
- break
- end
- end
-
- remove_private_comments(comment)
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/^#--.*?^#\+\+/m, '')
- comment.sub!(/^#--.*/m, '')
- end
-
-
-
- def get_symbol_or_name
- tk = get_tk
- case tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkId, TkOp
- tk.name
- when TkSTRING
- tk.text
- else
- raise "Name or symbol expected (got #{tk})"
- end
- end
-
- def parse_alias(context, single, tk, comment)
- skip_tkspace
- if (peek_tk.kind_of? TkLPAREN)
- get_tk
- skip_tkspace
- end
- new_name = get_symbol_or_name
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
- skip_tkspace
- if (peek_tk.kind_of? TkCOMMA)
- get_tk
- skip_tkspace
- end
- old_name = get_symbol_or_name
-
- al = Alias.new(get_tkread, old_name, new_name, comment)
- read_documentation_modifiers(al, ATTR_MODIFIERS)
- if al.document_self
- context.add_alias(al)
- end
- end
-
- def parse_yield_parameters
- parse_method_or_yield_parameters
- end
-
- def parse_yield(context, single, tk, method)
- if method.block_params.nil?
- get_tkread
- @scanner.instance_eval{@continue = false}
- method.block_params = parse_yield_parameters
- end
- end
-
- def parse_require(context, comment)
- skip_tkspace_comment
- tk = get_tk
- if tk.kind_of? TkLPAREN
- skip_tkspace_comment
- tk = get_tk
- end
-
- name = nil
- case tk
- when TkSTRING
- name = tk.text
-# when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
-# name = tk.name
- when TkDSTRING
- warn "Skipping require of dynamic string: #{tk.text}"
- # else
- # warn "'require' used as variable"
- end
- if name
- context.add_require(Require.new(name, comment))
- else
- unget_tk(tk)
- end
- end
-
- def parse_include(context, comment)
- loop do
- skip_tkspace_comment
- name = get_constant_with_optional_parens
- unless name.empty?
- context.add_include(Include.new(name, comment))
- end
- return unless peek_tk.kind_of?(TkCOMMA)
- get_tk
- end
- end
-
- def get_bool
- skip_tkspace
- tk = get_tk
- case tk
- when TkTRUE
- true
- when TkFALSE, TkNIL
- false
- else
- unget_tk tk
- true
- end
- end
-
- def parse_attr(context, single, tk, comment)
- args = parse_symbol_arg(1)
- if args.size > 0
- name = args[0]
- rw = "R"
- skip_tkspace(false)
- tk = get_tk
- if tk.kind_of? TkCOMMA
- rw = "RW" if get_bool
- else
- unget_tk tk
- end
- att = Attr.new(get_tkread, name, rw, comment)
- read_documentation_modifiers(att, ATTR_MODIFIERS)
- if att.document_self
- context.add_attribute(att)
- end
- else
- warn("'attr' ignored - looks like a variable")
- end
-
- end
-
- def parse_visibility(container, single, tk)
- singleton = (single == SINGLE)
- vis = case tk.name
- when "private" then :private
- when "protected" then :protected
- when "public" then :public
- when "private_class_method"
- singleton = true
- :private
- when "public_class_method"
- singleton = true
- :public
- else raise "Invalid visibility: #{tk.name}"
- end
-
- skip_tkspace_comment(false)
- case peek_tk
- # Ryan Davis suggested the extension to ignore modifiers, because he
- # often writes
- #
- # protected unless $TESTING
- #
- when TkNL, TkUNLESS_MOD, TkIF_MOD
-# error("Missing argument") if singleton
- container.ongoing_visibility = vis
- else
- args = parse_symbol_arg
- container.set_visibility_for(args, vis, singleton)
- end
- end
-
- def parse_attr_accessor(context, single, tk, comment)
- args = parse_symbol_arg
- read = get_tkread
- rw = "?"
-
- # If nodoc is given, don't document any of them
-
- tmp = CodeObject.new
- read_documentation_modifiers(tmp, ATTR_MODIFIERS)
- return unless tmp.document_self
-
- case tk.name
- when "attr_reader" then rw = "R"
- when "attr_writer" then rw = "W"
- when "attr_accessor" then rw = "RW"
- else
- rw = @options.extra_accessor_flags[tk.name]
- end
-
- for name in args
- att = Attr.new(get_tkread, name, rw, comment)
- context.add_attribute(att)
- end
- end
-
- def skip_tkspace_comment(skip_nl = true)
- loop do
- skip_tkspace(skip_nl)
- return unless peek_tk.kind_of? TkCOMMENT
- get_tk
- end
- end
-
- def parse_symbol_arg(no = nil)
-
- args = []
- skip_tkspace_comment
- case tk = get_tk
- when TkLPAREN
- loop do
- skip_tkspace_comment
- if tk1 = parse_symbol_in_arg
- args.push tk1
- break if no and args.size >= no
- end
-
- skip_tkspace_comment
- case tk2 = get_tk
- when TkRPAREN
- break
- when TkCOMMA
- else
- warn("unexpected token: '#{tk2.inspect}'") if $DEBUG
- break
- end
- end
- else
- unget_tk tk
- if tk = parse_symbol_in_arg
- args.push tk
- return args if no and args.size >= no
- end
-
- loop do
-# skip_tkspace_comment(false)
- skip_tkspace(false)
-
- tk1 = get_tk
- unless tk1.kind_of?(TkCOMMA)
- unget_tk tk1
- break
- end
-
- skip_tkspace_comment
- if tk = parse_symbol_in_arg
- args.push tk
- break if no and args.size >= no
- end
- end
- end
- args
- end
-
- def parse_symbol_in_arg
- case tk = get_tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkSTRING
- eval @read[-1]
- else
- warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG
- nil
- end
- end
- end
-
-end
diff --git a/lib/rdoc/parsers/parse_simple.rb b/lib/rdoc/parsers/parse_simple.rb
deleted file mode 100644
index 3f1a546964..0000000000
--- a/lib/rdoc/parsers/parse_simple.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# Parse a non-source file. We basically take the whole thing
-# as one big comment. If the first character in the file
-# is '#', we strip leading pound signs.
-
-
-require "rdoc/code_objects"
-require "rdoc/markup/simple_markup/preprocess"
-
-module RDoc
- # See rdoc/parsers/parse_c.rb
-
- class SimpleParser
-
- # prepare to parse a plain file
- def initialize(top_level, file_name, body, options, 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
- end
-
- def remove_private_comments(comment)
- comment.gsub(/^--.*?^\+\+/m, '').sub(/^--.*/m, '')
- end
- end
-end
diff --git a/lib/rdoc/parsers/parserfactory.rb b/lib/rdoc/parsers/parserfactory.rb
deleted file mode 100644
index 00a82cf4b1..0000000000
--- a/lib/rdoc/parsers/parserfactory.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-require "rdoc/parsers/parse_simple"
-
-module RDoc
-
- # A parser is simple a class that implements
- #
- # #initialize(file_name, body, options)
- #
- # and
- #
- # #scan
- #
- # The initialize method takes a file name to be used, the body of the
- # file, and an RDoc::Options object. The scan method is then called
- # to return an appropriately parsed TopLevel code object.
- #
- # The ParseFactory is used to redirect to the correct parser given a filename
- # extension. This magic works because individual parsers have to register
- # themselves with us as they are loaded in. The do this using the following
- # incantation
- #
- #
- # require "rdoc/parsers/parsefactory"
- #
- # module RDoc
- #
- # class XyzParser
- # extend ParseFactory <<<<
- # parse_files_matching /\.xyz$/ <<<<
- #
- # def initialize(file_name, body, options)
- # ...
- # end
- #
- # def scan
- # ...
- # end
- # end
- # end
- #
- # Just to make life interesting, if we suspect a plain text file, we
- # also look for a shebang line just in case it's a potential
- # shell script
-
-
-
- module ParserFactory
-
- @@parsers = []
-
- Parsers = Struct.new(:regexp, :parser)
-
- # Record the fact that a particular class parses files that
- # match a given extension
-
- def parse_files_matching(regexp)
- @@parsers.unshift Parsers.new(regexp, self)
- end
-
- # Return a parser that can handle a particular extension
-
- def ParserFactory.can_parse(file_name)
- @@parsers.find {|p| p.regexp.match(file_name) }
- end
-
- # Alias an extension to another extension. After this call,
- # files ending "new_ext" will be parsed using the same parser
- # as "old_ext"
-
- def ParserFactory.alias_extension(old_ext, new_ext)
- parser = ParserFactory.can_parse("xxx.#{old_ext}")
- return false unless parser
- @@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser)
- true
- end
-
- # Find the correct parser for a particular file name. Return a
- # SimpleParser for ones that we don't know
-
- def ParserFactory.parser_for(top_level, file_name, body, options, stats)
- # If no extension, look for shebang
- if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)}
- shebang = $1
- case shebang
- when %r{env\s+ruby}, %r{/ruby}
- file_name = "dummy.rb"
- end
- end
- parser_description = can_parse(file_name)
- if parser_description
- parser = parser_description.parser
- else
- parser = SimpleParser
- end
-
- parser.new(top_level, file_name, body, options, stats)
- end
- end
-end
diff --git a/lib/rdoc/rd.rb b/lib/rdoc/rd.rb
new file mode 100644
index 0000000000..0d3d3cea85
--- /dev/null
+++ b/lib/rdoc/rd.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+##
+# RDoc::RD implements the RD format from the rdtool gem.
+#
+# To choose RD as your only default format see
+# RDoc::Options@Saved+Options for instructions on setting up a
+# <code>.doc_options</code> file to store your project default.
+#
+# == LICENSE
+#
+# The grammar that produces RDoc::RD::BlockParser and RDoc::RD::InlineParser
+# is included in RDoc under the Ruby License.
+#
+# You can find the original source for rdtool at
+# https://github.com/uwabami/rdtool/
+#
+# You can use, re-distribute or change these files under Ruby's License or GPL.
+#
+# 1. You may make and give away verbatim copies of the source form of the
+# software without restriction, provided that you duplicate all of the
+# original copyright notices and associated disclaimers.
+#
+# 2. You may modify your copy of the software in any way, provided that
+# you do at least ONE of the following:
+#
+# a. place your modifications in the Public Domain or otherwise
+# make them Freely Available, such as by posting said
+# modifications to Usenet or an equivalent medium, or by allowing
+# the author to include your modifications in the software.
+#
+# b. use the modified software only within your corporation or
+# organization.
+#
+# c. give non-standard binaries non-standard names, with
+# instructions on where to get the original software distribution.
+#
+# d. make other distribution arrangements with the author.
+#
+# 3. You may distribute the software in object code or binary form,
+# provided that you do at least ONE of the following:
+#
+# a. distribute the binaries and library files of the software,
+# together with instructions (in the manual page or equivalent)
+# on where to get the original distribution.
+#
+# b. accompany the distribution with the machine-readable source of
+# the software.
+#
+# c. give non-standard binaries non-standard names, with
+# instructions on where to get the original software distribution.
+#
+# d. make other distribution arrangements with the author.
+#
+# 4. You may modify and include the part of the software into any other
+# software (possibly commercial). But some files in the distribution
+# are not written by the author, so that they are not under these terms.
+#
+# For the list of those files and their copying conditions, see the
+# file LEGAL.
+#
+# 5. The scripts and library files supplied as input to or produced as
+# output from the software do not automatically fall under the
+# copyright of the software, but belong to whomever generated them,
+# and may be sold commercially, and may be aggregated with this
+# software.
+#
+# 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+
+class RDoc::RD
+
+ ##
+ # Parses +rd+ source and returns an RDoc::Markup::Document. If the
+ # <tt>=begin</tt> or <tt>=end</tt> lines are missing they will be added.
+
+ def self.parse rd
+ rd = rd.lines.to_a
+
+ if rd.find { |i| /\S/ === i } and !rd.find{|i| /^=begin\b/ === i } then
+ rd.unshift("=begin\n").push("=end\n")
+ end
+
+ parser = RDoc::RD::BlockParser.new
+ document = parser.parse rd
+
+ # isn't this always true?
+ document.parts.shift if RDoc::Markup::BlankLine === document.parts.first
+ document.parts.pop if RDoc::Markup::BlankLine === document.parts.last
+
+ document
+ end
+
+ autoload :BlockParser, 'rdoc/rd/block_parser'
+ autoload :InlineParser, 'rdoc/rd/inline_parser'
+ autoload :Inline, 'rdoc/rd/inline'
+
+end
+
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
new file mode 100644
index 0000000000..3f4941168f
--- /dev/null
+++ b/lib/rdoc/rd/block_parser.rb
@@ -0,0 +1,1055 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by Racc 1.4.14
+# from Racc grammer file "".
+#
+
+require 'racc/parser.rb'
+
+class RDoc::RD
+
+##
+# RD format parser for headings, paragraphs, lists, verbatim sections that
+# exist as blocks.
+
+class BlockParser < Racc::Parser
+
+
+# :stopdoc:
+
+TMPFILE = ["rdtmp", $$, 0]
+
+MARK_TO_LEVEL = {
+ '=' => 1,
+ '==' => 2,
+ '===' => 3,
+ '====' => 4,
+ '+' => 5,
+ '++' => 6,
+}
+
+# :startdoc:
+
+##
+# Footnotes for this document
+
+attr_reader :footnotes
+
+##
+# Labels for items in this document
+
+attr_reader :labels
+
+##
+# Path to find included files in
+
+attr_accessor :include_path
+
+##
+# Creates a new RDoc::RD::BlockParser. Use #parse to parse an rd-format
+# document.
+
+def initialize
+ @inline_parser = RDoc::RD::InlineParser.new self
+ @include_path = []
+
+ # for testing
+ @footnotes = []
+ @labels = {}
+end
+
+##
+# Parses +src+ and returns an RDoc::Markup::Document.
+
+def parse src
+ @src = src
+ @src.push false
+
+ @footnotes = []
+ @labels = {}
+
+ # @i: index(line no.) of src
+ @i = 0
+
+ # stack for current indentation
+ @indent_stack = []
+
+ # how indented.
+ @current_indent = @indent_stack.join("")
+
+ # RDoc::RD::BlockParser for tmp src
+ @subparser = nil
+
+ # which part is in now
+ @in_part = nil
+ @part_content = []
+
+ @in_verbatim = false
+
+ @yydebug = true
+
+ document = do_parse
+
+ unless @footnotes.empty? then
+ blankline = document.parts.pop
+
+ document.parts << RDoc::Markup::Rule.new(1)
+ document.parts.concat @footnotes
+
+ document.parts.push blankline
+ end
+
+ document
+end
+
+##
+# Returns the next token from the document
+
+def next_token # :nodoc:
+ # preprocessing
+ # if it is not in RD part
+ # => method
+ while @in_part != "rd"
+ line = @src[@i]
+ @i += 1 # next line
+
+ case line
+ # src end
+ when false
+ return [false, false]
+ # RD part begin
+ when /^=begin\s*(?:\bRD\b.*)?\s*$/
+ if @in_part # if in non-RD part
+ @part_content.push(line)
+ else
+ @in_part = "rd"
+ return [:WHITELINE, "=begin\n"] # <= for textblockand
+ end
+ # non-RD part begin
+ when /^=begin\s+(\w+)/
+ part = $1
+ if @in_part # if in non-RD part
+ @part_content.push(line)
+ else
+ @in_part = part if @tree.filter[part] # if filter exists
+# p "BEGIN_PART: #{@in_part}" # DEBUG
+ end
+ # non-RD part end
+ when /^=end/
+ if @in_part # if in non-RD part
+# p "END_PART: #{@in_part}" # DEBUG
+ # make Part-in object
+ part = RDoc::RD::Part.new(@part_content.join(""), @tree, "r")
+ @part_content.clear
+ # call filter, part_out is output(Part object)
+ part_out = @tree.filter[@in_part].call(part)
+
+ if @tree.filter[@in_part].mode == :rd # if output is RD formatted
+ subtree = parse_subtree(part_out.to_a)
+ else # if output is target formatted
+ basename = TMPFILE.join('.')
+ TMPFILE[-1] += 1
+ tmpfile = open(@tree.tmp_dir + "/" + basename + ".#{@in_part}", "w")
+ tmpfile.print(part_out)
+ tmpfile.close
+ subtree = parse_subtree(["=begin\n", "<<< #{basename}\n", "=end\n"])
+ end
+ @in_part = nil
+ return [:SUBTREE, subtree]
+ end
+ else
+ if @in_part # if in non-RD part
+ @part_content.push(line)
+ end
+ end
+ end
+
+ @current_indent = @indent_stack.join("")
+ line = @src[@i]
+ case line
+ when false
+ if_current_indent_equal("") do
+ [false, false]
+ end
+ when /^=end/
+ if_current_indent_equal("") do
+ @in_part = nil
+ [:WHITELINE, "=end"] # MUST CHANGE??
+ end
+ when /^\s*$/
+ @i += 1 # next line
+ return [:WHITELINE, ':WHITELINE']
+ when /^\#/ # comment line
+ @i += 1 # next line
+ self.next_token()
+ when /^(={1,4})(?!=)\s*(?=\S)/, /^(\+{1,2})(?!\+)\s*(?=\S)/
+ rest = $' # '
+ rest.strip!
+ mark = $1
+ if_current_indent_equal("") do
+ return [:HEADLINE, [MARK_TO_LEVEL[mark], rest]]
+ end
+ when /^<<<\s*(\S+)/
+ file = $1
+ if_current_indent_equal("") do
+ suffix = file[-3 .. -1]
+ if suffix == ".rd" or suffix == ".rb"
+ subtree = parse_subtree(get_included(file))
+ [:SUBTREE, subtree]
+ else
+ [:INCLUDE, file]
+ end
+ end
+ when /^(\s*)\*(\s*)/
+ rest = $' # '
+ newIndent = $2
+ if_current_indent_equal($1) do
+ if @in_verbatim
+ [:STRINGLINE, line]
+ else
+ @indent_stack.push("\s" << newIndent)
+ [:ITEMLISTLINE, rest]
+ end
+ end
+ when /^(\s*)(\(\d+\))(\s*)/
+ rest = $' # '
+ mark = $2
+ newIndent = $3
+ if_current_indent_equal($1) do
+ if @in_verbatim
+ [:STRINGLINE, line]
+ else
+ @indent_stack.push("\s" * mark.size << newIndent)
+ [:ENUMLISTLINE, rest]
+ end
+ end
+ when /^(\s*):(\s*)/
+ rest = $' # '
+ newIndent = $2
+ if_current_indent_equal($1) do
+ if @in_verbatim
+ [:STRINGLINE, line]
+ else
+ @indent_stack.push("\s#{$2}")
+ [:DESCLISTLINE, rest]
+ end
+ end
+ when /^(\s*)---(?!-|\s*$)/
+ indent = $1
+ rest = $'
+ /\s*/ === rest
+ term = $'
+ new_indent = $&
+ if_current_indent_equal(indent) do
+ if @in_verbatim
+ [:STRINGLINE, line]
+ else
+ @indent_stack.push("\s\s\s" + new_indent)
+ [:METHODLISTLINE, term]
+ end
+ end
+ when /^(\s*)/
+ if_current_indent_equal($1) do
+ [:STRINGLINE, line]
+ end
+ else
+ raise "[BUG] parsing error may occurred."
+ end
+end
+
+##
+# Yields to the given block if +indent+ matches the current indent, otherwise
+# an indentation token is processed.
+
+def if_current_indent_equal(indent)
+ indent = indent.sub(/\t/, "\s" * 8)
+ if @current_indent == indent
+ @i += 1 # next line
+ yield
+ elsif indent.index(@current_indent) == 0
+ @indent_stack.push(indent[@current_indent.size .. -1])
+ [:INDENT, ":INDENT"]
+ else
+ @indent_stack.pop
+ [:DEDENT, ":DEDENT"]
+ end
+end
+private :if_current_indent_equal
+
+##
+# Cuts off excess whitespace in +src+
+
+def cut_off(src)
+ ret = []
+ whiteline_buf = []
+
+ line = src.shift
+ /^\s*/ =~ line
+
+ indent = Regexp.quote($&)
+ ret.push($')
+
+ while line = src.shift
+ if /^(\s*)$/ =~ line
+ whiteline_buf.push(line)
+ elsif /^#{indent}/ =~ line
+ unless whiteline_buf.empty?
+ ret.concat(whiteline_buf)
+ whiteline_buf.clear
+ end
+ ret.push($')
+ else
+ raise "[BUG]: probably Parser Error while cutting off.\n"
+ end
+ end
+ ret
+end
+private :cut_off
+
+def set_term_to_element(parent, term)
+# parent.set_term_under_document_struct(term, @tree.document_struct)
+ parent.set_term_without_document_struct(term)
+end
+private :set_term_to_element
+
+##
+# Raises a ParseError when invalid formatting is found
+
+def on_error(et, ev, _values)
+ prv, cur, nxt = format_line_num(@i, @i+1, @i+2)
+
+ raise ParseError, <<Msg
+
+RD syntax error: line #{@i+1}:
+ #{prv} |#{@src[@i-1].chomp}
+ #{cur}=>|#{@src[@i].chomp}
+ #{nxt} |#{@src[@i+1].chomp}
+
+Msg
+end
+
+##
+# Current line number
+
+def line_index
+ @i
+end
+
+##
+# Parses subtree +src+
+
+def parse_subtree src
+ @subparser ||= RDoc::RD::BlockParser.new
+
+ @subparser.parse src
+end
+private :parse_subtree
+
+##
+# Retrieves the content for +file+ from the include_path
+
+def get_included(file)
+ included = []
+
+ @include_path.each do |dir|
+ file_name = File.join dir, file
+
+ if File.exist? file_name then
+ included = IO.readlines file_name
+ break
+ end
+ end
+
+ included
+end
+private :get_included
+
+##
+# Formats line numbers +line_numbers+ prettily
+
+def format_line_num(*line_numbers)
+ width = line_numbers.collect{|i| i.to_s.length }.max
+ line_numbers.collect{|i| sprintf("%#{width}d", i) }
+end
+private :format_line_num
+
+##
+# Retrieves the content of +values+ as a single String
+
+def content values
+ values.map { |value| value.content }.join
+end
+
+##
+# Creates a paragraph for +value+
+
+def paragraph value
+ content = cut_off(value).join(' ').rstrip
+ contents = @inline_parser.parse content
+
+ RDoc::Markup::Paragraph.new(*contents)
+end
+
+##
+# Adds footnote +content+ to the document
+
+def add_footnote content
+ index = @footnotes.length / 2 + 1
+
+ footmark_link = "{^#{index}}[rdoc-label:footmark-#{index}:foottext-#{index}]"
+
+ @footnotes << RDoc::Markup::Paragraph.new(footmark_link, ' ', *content)
+ @footnotes << RDoc::Markup::BlankLine.new
+
+ index
+end
+
+##
+# Adds label +label+ to the document
+
+def add_label label
+ @labels[label] = true
+
+ label
+end
+
+# :stopdoc:
+
+##### State transition tables begin ###
+
+racc_action_table = [
+ 34, 35, 30, 33, 40, 34, 35, 30, 33, 40,
+ 65, 34, 35, 30, 33, 14, 73, 14, 54, 76,
+ 15, 88, 34, 35, 30, 33, 14, 73, 77, 33,
+ 54, 15, 34, 35, 30, 33, 14, 73, 81, 38,
+ 38, 15, 34, 35, 30, 33, 14, 73, 40, 36,
+ 83, 15, 34, 35, 30, 33, 54, 47, 30, 35,
+ 34, 15, 34, 35, 30, 33, 14, 73, 38, 67,
+ 59, 15, 34, 35, 30, 33, 14, 9, 10, 11,
+ 12, 15, 34, 35, 30, 33, 14, 73, 14, nil,
+ nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
+ nil, 15, 34, 35, 30, 33, nil, 47, nil, nil,
+ nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
+ nil, 15, 34, 35, 30, 33, 14, 73, nil, nil,
+ nil, 15, 34, 35, 30, 33, 14, 9, 10, 11,
+ 12, 15, 34, 35, 30, 33, 14, 73, 61, 63,
+ nil, 15, 14, 62, 60, 61, 63, 79, 61, 63,
+ 62, 87, nil, 62, 34, 35, 30, 33 ]
+
+racc_action_check = [
+ 41, 41, 41, 41, 41, 15, 15, 15, 15, 15,
+ 41, 86, 86, 86, 86, 86, 86, 34, 33, 49,
+ 86, 86, 85, 85, 85, 85, 85, 85, 51, 31,
+ 54, 85, 79, 79, 79, 79, 79, 79, 56, 57,
+ 58, 79, 78, 78, 78, 78, 78, 78, 62, 1,
+ 66, 78, 24, 24, 24, 24, 30, 24, 28, 25,
+ 22, 24, 75, 75, 75, 75, 75, 75, 13, 44,
+ 36, 75, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 46, 46, 46, 46, 46, 46, 35, nil,
+ nil, 46, 45, 45, 45, 45, 45, 45, nil, nil,
+ nil, 45, 27, 27, 27, 27, nil, 27, nil, nil,
+ nil, 27, 74, 74, 74, 74, 74, 74, nil, nil,
+ nil, 74, 68, 68, 68, 68, 68, 68, nil, nil,
+ nil, 68, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 47, 47, 47, 47, 47, 47, 39, 39,
+ nil, 47, 52, 39, 39, 82, 82, 52, 64, 64,
+ 82, 82, nil, 64, 20, 20, 20, 20 ]
+
+racc_action_pointer = [
+ 129, 49, 69, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, 61, nil, 2, nil, nil, nil, nil,
+ 161, nil, 57, nil, 49, 55, nil, 99, 53, nil,
+ 48, 23, nil, 10, 10, 81, 70, nil, nil, 141,
+ nil, -3, nil, nil, 56, 89, 79, 139, nil, 6,
+ nil, 15, 145, nil, 22, nil, 25, 32, 33, nil,
+ nil, nil, 41, nil, 151, nil, 37, nil, 119, nil,
+ nil, nil, nil, nil, 109, 59, nil, nil, 39, 29,
+ nil, nil, 148, nil, nil, 19, 8, nil, nil ]
+
+racc_action_default = [
+ -2, -73, -1, -4, -5, -6, -7, -8, -9, -10,
+ -11, -12, -13, -14, -16, -73, -23, -24, -25, -26,
+ -27, -31, -32, -34, -72, -36, -38, -72, -40, -42,
+ -59, -44, -46, -59, -63, -65, -73, -3, -15, -73,
+ -22, -73, -30, -33, -73, -69, -70, -71, -37, -73,
+ -41, -73, -51, -58, -61, -45, -73, -62, -64, 89,
+ -17, -19, -73, -21, -18, -28, -73, -35, -66, -53,
+ -54, -55, -56, -57, -67, -68, -39, -43, -49, -73,
+ -60, -47, -73, -29, -52, -48, -73, -20, -50 ]
+
+racc_goto_table = [
+ 4, 39, 4, 68, 74, 75, 6, 5, 6, 5,
+ 44, 42, 51, 49, 3, 56, 37, 57, 58, 80,
+ 2, 66, 84, 41, 43, 48, 50, 64, 84, 84,
+ 46, 45, 42, 46, 45, 55, 85, 86, 1, 84,
+ 84, nil, nil, nil, nil, nil, nil, nil, 82, nil,
+ nil, nil, 78 ]
+
+racc_goto_check = [
+ 4, 10, 4, 31, 31, 31, 6, 5, 6, 5,
+ 21, 12, 27, 21, 3, 27, 3, 9, 9, 33,
+ 2, 11, 32, 17, 19, 23, 26, 10, 32, 32,
+ 6, 5, 12, 6, 5, 29, 31, 31, 1, 32,
+ 32, nil, nil, nil, nil, nil, nil, nil, 10, nil,
+ nil, nil, 4 ]
+
+racc_goto_pointer = [
+ nil, 38, 20, 14, 0, 7, 6, nil, nil, -17,
+ -14, -20, -9, nil, nil, nil, nil, 8, nil, 2,
+ nil, -14, nil, 0, nil, nil, -2, -18, nil, 4,
+ nil, -42, -46, -35 ]
+
+racc_goto_default = [
+ nil, nil, nil, nil, 70, 71, 72, 7, 8, 13,
+ nil, nil, 21, 16, 17, 18, 19, 20, 22, 23,
+ 24, nil, 25, 26, 27, 28, 29, nil, 31, 32,
+ 52, nil, 69, 53 ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 15, :_reduce_1,
+ 0, 15, :_reduce_2,
+ 2, 16, :_reduce_3,
+ 1, 16, :_reduce_4,
+ 1, 17, :_reduce_5,
+ 1, 17, :_reduce_6,
+ 1, 17, :_reduce_none,
+ 1, 17, :_reduce_8,
+ 1, 17, :_reduce_9,
+ 1, 17, :_reduce_10,
+ 1, 17, :_reduce_11,
+ 1, 21, :_reduce_12,
+ 1, 22, :_reduce_13,
+ 1, 18, :_reduce_14,
+ 2, 23, :_reduce_15,
+ 1, 23, :_reduce_16,
+ 3, 19, :_reduce_17,
+ 1, 25, :_reduce_18,
+ 2, 24, :_reduce_19,
+ 4, 24, :_reduce_20,
+ 2, 24, :_reduce_21,
+ 1, 24, :_reduce_22,
+ 1, 26, :_reduce_none,
+ 1, 26, :_reduce_none,
+ 1, 26, :_reduce_none,
+ 1, 26, :_reduce_none,
+ 1, 20, :_reduce_27,
+ 3, 20, :_reduce_28,
+ 4, 20, :_reduce_29,
+ 2, 31, :_reduce_30,
+ 1, 31, :_reduce_31,
+ 1, 27, :_reduce_32,
+ 2, 32, :_reduce_33,
+ 1, 32, :_reduce_34,
+ 3, 33, :_reduce_35,
+ 1, 28, :_reduce_36,
+ 2, 36, :_reduce_37,
+ 1, 36, :_reduce_38,
+ 3, 37, :_reduce_39,
+ 1, 29, :_reduce_40,
+ 2, 39, :_reduce_41,
+ 1, 39, :_reduce_42,
+ 3, 40, :_reduce_43,
+ 1, 30, :_reduce_44,
+ 2, 42, :_reduce_45,
+ 1, 42, :_reduce_46,
+ 3, 43, :_reduce_47,
+ 3, 41, :_reduce_48,
+ 2, 41, :_reduce_49,
+ 4, 41, :_reduce_50,
+ 1, 41, :_reduce_51,
+ 2, 45, :_reduce_52,
+ 1, 45, :_reduce_none,
+ 1, 46, :_reduce_54,
+ 1, 46, :_reduce_55,
+ 1, 46, :_reduce_none,
+ 1, 46, :_reduce_57,
+ 1, 44, :_reduce_none,
+ 0, 44, :_reduce_none,
+ 2, 47, :_reduce_none,
+ 1, 47, :_reduce_none,
+ 2, 34, :_reduce_62,
+ 1, 34, :_reduce_63,
+ 2, 38, :_reduce_64,
+ 1, 38, :_reduce_65,
+ 2, 35, :_reduce_66,
+ 2, 35, :_reduce_67,
+ 2, 35, :_reduce_68,
+ 1, 35, :_reduce_69,
+ 1, 35, :_reduce_none,
+ 1, 35, :_reduce_71,
+ 0, 35, :_reduce_72 ]
+
+racc_reduce_n = 73
+
+racc_shift_n = 89
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ :DUMMY => 2,
+ :ITEMLISTLINE => 3,
+ :ENUMLISTLINE => 4,
+ :DESCLISTLINE => 5,
+ :METHODLISTLINE => 6,
+ :STRINGLINE => 7,
+ :WHITELINE => 8,
+ :SUBTREE => 9,
+ :HEADLINE => 10,
+ :INCLUDE => 11,
+ :INDENT => 12,
+ :DEDENT => 13 }
+
+racc_nt_base = 14
+
+racc_use_result_var = true
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "DUMMY",
+ "ITEMLISTLINE",
+ "ENUMLISTLINE",
+ "DESCLISTLINE",
+ "METHODLISTLINE",
+ "STRINGLINE",
+ "WHITELINE",
+ "SUBTREE",
+ "HEADLINE",
+ "INCLUDE",
+ "INDENT",
+ "DEDENT",
+ "$start",
+ "document",
+ "blocks",
+ "block",
+ "textblock",
+ "verbatim",
+ "lists",
+ "headline",
+ "include",
+ "textblockcontent",
+ "verbatimcontent",
+ "verbatim_after_lists",
+ "list",
+ "itemlist",
+ "enumlist",
+ "desclist",
+ "methodlist",
+ "lists2",
+ "itemlistitems",
+ "itemlistitem",
+ "first_textblock_in_itemlist",
+ "other_blocks_in_list",
+ "enumlistitems",
+ "enumlistitem",
+ "first_textblock_in_enumlist",
+ "desclistitems",
+ "desclistitem",
+ "description_part",
+ "methodlistitems",
+ "methodlistitem",
+ "whitelines",
+ "blocks_in_list",
+ "block_in_list",
+ "whitelines2" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+def _reduce_1(val, _values, result)
+ result = RDoc::Markup::Document.new(*val[0])
+ result
+end
+
+def _reduce_2(val, _values, result)
+ raise ParseError, "file empty"
+ result
+end
+
+def _reduce_3(val, _values, result)
+ result = val[0].concat val[1]
+ result
+end
+
+def _reduce_4(val, _values, result)
+ result = val[0]
+ result
+end
+
+def _reduce_5(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_6(val, _values, result)
+ result = val
+ result
+end
+
+# reduce 7 omitted
+
+def _reduce_8(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_9(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_10(val, _values, result)
+ result = [RDoc::Markup::BlankLine.new]
+ result
+end
+
+def _reduce_11(val, _values, result)
+ result = val[0].parts
+ result
+end
+
+def _reduce_12(val, _values, result)
+ # val[0] is like [level, title]
+ title = @inline_parser.parse(val[0][1])
+ result = RDoc::Markup::Heading.new(val[0][0], title)
+
+ result
+end
+
+def _reduce_13(val, _values, result)
+ result = RDoc::Markup::Include.new val[0], @include_path
+
+ result
+end
+
+def _reduce_14(val, _values, result)
+ # val[0] is Array of String
+ result = paragraph val[0]
+
+ result
+end
+
+def _reduce_15(val, _values, result)
+ result << val[1].rstrip
+ result
+end
+
+def _reduce_16(val, _values, result)
+ result = [val[0].rstrip]
+ result
+end
+
+def _reduce_17(val, _values, result)
+ # val[1] is Array of String
+ content = cut_off val[1]
+ result = RDoc::Markup::Verbatim.new(*content)
+
+ # imform to lexer.
+ @in_verbatim = false
+
+ result
+end
+
+def _reduce_18(val, _values, result)
+ # val[0] is Array of String
+ content = cut_off val[0]
+ result = RDoc::Markup::Verbatim.new(*content)
+
+ # imform to lexer.
+ @in_verbatim = false
+
+ result
+end
+
+def _reduce_19(val, _values, result)
+ result << val[1]
+
+ result
+end
+
+def _reduce_20(val, _values, result)
+ result.concat val[2]
+
+ result
+end
+
+def _reduce_21(val, _values, result)
+ result << "\n"
+
+ result
+end
+
+def _reduce_22(val, _values, result)
+ result = val
+ # inform to lexer.
+ @in_verbatim = true
+
+ result
+end
+
+# reduce 23 omitted
+
+# reduce 24 omitted
+
+# reduce 25 omitted
+
+# reduce 26 omitted
+
+def _reduce_27(val, _values, result)
+ result = val[0]
+
+ result
+end
+
+def _reduce_28(val, _values, result)
+ result = val[1]
+
+ result
+end
+
+def _reduce_29(val, _values, result)
+ result = val[1].push(val[2])
+
+ result
+end
+
+def _reduce_30(val, _values, result)
+ result = val[0] << val[1]
+ result
+end
+
+def _reduce_31(val, _values, result)
+ result = [val[0]]
+ result
+end
+
+def _reduce_32(val, _values, result)
+ result = RDoc::Markup::List.new :BULLET, *val[0]
+
+ result
+end
+
+def _reduce_33(val, _values, result)
+ result.push(val[1])
+ result
+end
+
+def _reduce_34(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_35(val, _values, result)
+ result = RDoc::Markup::ListItem.new nil, val[0], *val[1]
+
+ result
+end
+
+def _reduce_36(val, _values, result)
+ result = RDoc::Markup::List.new :NUMBER, *val[0]
+
+ result
+end
+
+def _reduce_37(val, _values, result)
+ result.push(val[1])
+ result
+end
+
+def _reduce_38(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_39(val, _values, result)
+ result = RDoc::Markup::ListItem.new nil, val[0], *val[1]
+
+ result
+end
+
+def _reduce_40(val, _values, result)
+ result = RDoc::Markup::List.new :NOTE, *val[0]
+
+ result
+end
+
+def _reduce_41(val, _values, result)
+ result.push(val[1])
+ result
+end
+
+def _reduce_42(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_43(val, _values, result)
+ term = @inline_parser.parse val[0].strip
+
+ result = RDoc::Markup::ListItem.new term, *val[1]
+
+ result
+end
+
+def _reduce_44(val, _values, result)
+ result = RDoc::Markup::List.new :LABEL, *val[0]
+
+ result
+end
+
+def _reduce_45(val, _values, result)
+ result.push(val[1])
+ result
+end
+
+def _reduce_46(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_47(val, _values, result)
+ result = RDoc::Markup::ListItem.new "<tt>#{val[0].strip}</tt>", *val[1]
+
+ result
+end
+
+def _reduce_48(val, _values, result)
+ result = [val[1]].concat(val[2])
+
+ result
+end
+
+def _reduce_49(val, _values, result)
+ result = [val[1]]
+
+ result
+end
+
+def _reduce_50(val, _values, result)
+ result = val[2]
+
+ result
+end
+
+def _reduce_51(val, _values, result)
+ result = []
+
+ result
+end
+
+def _reduce_52(val, _values, result)
+ result.concat val[1]
+ result
+end
+
+# reduce 53 omitted
+
+def _reduce_54(val, _values, result)
+ result = val
+ result
+end
+
+def _reduce_55(val, _values, result)
+ result = val
+ result
+end
+
+# reduce 56 omitted
+
+def _reduce_57(val, _values, result)
+ result = []
+ result
+end
+
+# reduce 58 omitted
+
+# reduce 59 omitted
+
+# reduce 60 omitted
+
+# reduce 61 omitted
+
+def _reduce_62(val, _values, result)
+ result = paragraph [val[0]].concat(val[1])
+
+ result
+end
+
+def _reduce_63(val, _values, result)
+ result = paragraph [val[0]]
+
+ result
+end
+
+def _reduce_64(val, _values, result)
+ result = paragraph [val[0]].concat(val[1])
+
+ result
+end
+
+def _reduce_65(val, _values, result)
+ result = paragraph [val[0]]
+
+ result
+end
+
+def _reduce_66(val, _values, result)
+ result = [val[0]].concat(val[1])
+
+ result
+end
+
+def _reduce_67(val, _values, result)
+ result.concat val[1]
+ result
+end
+
+def _reduce_68(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_69(val, _values, result)
+ result = val
+ result
+end
+
+# reduce 70 omitted
+
+def _reduce_71(val, _values, result)
+ result = []
+ result
+end
+
+def _reduce_72(val, _values, result)
+ result = []
+ result
+end
+
+def _reduce_none(val, _values, result)
+ val[0]
+end
+
+end # class BlockParser
+
+end
diff --git a/lib/rdoc/rd/inline.rb b/lib/rdoc/rd/inline.rb
new file mode 100644
index 0000000000..e5cb545728
--- /dev/null
+++ b/lib/rdoc/rd/inline.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+##
+# Inline keeps track of markup and labels to create proper links.
+
+class RDoc::RD::Inline
+
+ ##
+ # The text of the reference
+
+ attr_reader :reference
+
+ ##
+ # The markup of this reference in RDoc format
+
+ attr_reader :rdoc
+
+ ##
+ # Creates a new Inline for +rdoc+ and +reference+.
+ #
+ # +rdoc+ may be another Inline or a String. If +reference+ is not given it
+ # will use the text from +rdoc+.
+
+ def self.new rdoc, reference = rdoc
+ if self === rdoc and reference.equal? rdoc then
+ rdoc
+ else
+ super
+ end
+ end
+
+ ##
+ # Initializes the Inline with +rdoc+ and +inline+
+
+ def initialize rdoc, reference # :not-new:
+ @reference = reference.equal?(rdoc) ? reference.dup : reference
+
+ # unpack
+ @reference = @reference.reference if self.class === @reference
+ @rdoc = rdoc
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @reference == other.reference and @rdoc == other.rdoc
+ end
+
+ ##
+ # Appends +more+ to this inline. +more+ may be a String or another Inline.
+
+ def append more
+ case more
+ when String then
+ @reference += more
+ @rdoc += more
+ when RDoc::RD::Inline then
+ @reference += more.reference
+ @rdoc += more.rdoc
+ else
+ raise "unknown thingy #{more}"
+ end
+
+ self
+ end
+
+ def inspect # :nodoc:
+ "(inline: #{self})"
+ end
+
+ alias to_s rdoc # :nodoc:
+
+end
+
diff --git a/lib/rdoc/rd/inline_parser.rb b/lib/rdoc/rd/inline_parser.rb
new file mode 100644
index 0000000000..783a5a7c7e
--- /dev/null
+++ b/lib/rdoc/rd/inline_parser.rb
@@ -0,0 +1,1207 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by Racc 1.4.14
+# from Racc grammer file "".
+#
+
+require 'racc/parser.rb'
+
+require 'strscan'
+
+class RDoc::RD
+
+##
+# RD format parser for inline markup such as emphasis, links, footnotes, etc.
+
+class InlineParser < Racc::Parser
+
+
+# :stopdoc:
+
+EM_OPEN = '((*'
+EM_OPEN_RE = /\A#{Regexp.quote(EM_OPEN)}/
+EM_CLOSE = '*))'
+EM_CLOSE_RE = /\A#{Regexp.quote(EM_CLOSE)}/
+CODE_OPEN = '(({'
+CODE_OPEN_RE = /\A#{Regexp.quote(CODE_OPEN)}/
+CODE_CLOSE = '}))'
+CODE_CLOSE_RE = /\A#{Regexp.quote(CODE_CLOSE)}/
+VAR_OPEN = '((|'
+VAR_OPEN_RE = /\A#{Regexp.quote(VAR_OPEN)}/
+VAR_CLOSE = '|))'
+VAR_CLOSE_RE = /\A#{Regexp.quote(VAR_CLOSE)}/
+KBD_OPEN = '((%'
+KBD_OPEN_RE = /\A#{Regexp.quote(KBD_OPEN)}/
+KBD_CLOSE = '%))'
+KBD_CLOSE_RE = /\A#{Regexp.quote(KBD_CLOSE)}/
+INDEX_OPEN = '((:'
+INDEX_OPEN_RE = /\A#{Regexp.quote(INDEX_OPEN)}/
+INDEX_CLOSE = ':))'
+INDEX_CLOSE_RE = /\A#{Regexp.quote(INDEX_CLOSE)}/
+REF_OPEN = '((<'
+REF_OPEN_RE = /\A#{Regexp.quote(REF_OPEN)}/
+REF_CLOSE = '>))'
+REF_CLOSE_RE = /\A#{Regexp.quote(REF_CLOSE)}/
+FOOTNOTE_OPEN = '((-'
+FOOTNOTE_OPEN_RE = /\A#{Regexp.quote(FOOTNOTE_OPEN)}/
+FOOTNOTE_CLOSE = '-))'
+FOOTNOTE_CLOSE_RE = /\A#{Regexp.quote(FOOTNOTE_CLOSE)}/
+VERB_OPEN = "(('"
+VERB_OPEN_RE = /\A#{Regexp.quote(VERB_OPEN)}/
+VERB_CLOSE = "'))"
+VERB_CLOSE_RE = /\A#{Regexp.quote(VERB_CLOSE)}/
+
+BAR = "|"
+BAR_RE = /\A#{Regexp.quote(BAR)}/
+QUOTE = '"'
+QUOTE_RE = /\A#{Regexp.quote(QUOTE)}/
+SLASH = "/"
+SLASH_RE = /\A#{Regexp.quote(SLASH)}/
+BACK_SLASH = "\\"
+BACK_SLASH_RE = /\A#{Regexp.quote(BACK_SLASH)}/
+URL = "URL:"
+URL_RE = /\A#{Regexp.quote(URL)}/
+
+other_re_mode = Regexp::EXTENDED
+other_re_mode |= Regexp::MULTILINE
+
+OTHER_RE = Regexp.new(
+ "\\A.+?(?=#{Regexp.quote(EM_OPEN)}|#{Regexp.quote(EM_CLOSE)}|
+ #{Regexp.quote(CODE_OPEN)}|#{Regexp.quote(CODE_CLOSE)}|
+ #{Regexp.quote(VAR_OPEN)}|#{Regexp.quote(VAR_CLOSE)}|
+ #{Regexp.quote(KBD_OPEN)}|#{Regexp.quote(KBD_CLOSE)}|
+ #{Regexp.quote(INDEX_OPEN)}|#{Regexp.quote(INDEX_CLOSE)}|
+ #{Regexp.quote(REF_OPEN)}|#{Regexp.quote(REF_CLOSE)}|
+ #{Regexp.quote(FOOTNOTE_OPEN)}|#{Regexp.quote(FOOTNOTE_CLOSE)}|
+ #{Regexp.quote(VERB_OPEN)}|#{Regexp.quote(VERB_CLOSE)}|
+ #{Regexp.quote(BAR)}|
+ #{Regexp.quote(QUOTE)}|
+ #{Regexp.quote(SLASH)}|
+ #{Regexp.quote(BACK_SLASH)}|
+ #{Regexp.quote(URL)})", other_re_mode)
+
+# :startdoc:
+
+##
+# Creates a new parser for inline markup in the rd format. The +block_parser+
+# is used to for footnotes and labels in the inline text.
+
+def initialize block_parser
+ @block_parser = block_parser
+end
+
+##
+# Parses the +inline+ text from RD format into RDoc format.
+
+def parse inline
+ @inline = inline
+ @src = StringScanner.new inline
+ @pre = ""
+ @yydebug = true
+ do_parse.to_s
+end
+
+##
+# Returns the next token from the inline text
+
+def next_token
+ return [false, false] if @src.eos?
+# p @src.rest if @yydebug
+ if ret = @src.scan(EM_OPEN_RE)
+ @pre << ret
+ [:EM_OPEN, ret]
+ elsif ret = @src.scan(EM_CLOSE_RE)
+ @pre << ret
+ [:EM_CLOSE, ret]
+ elsif ret = @src.scan(CODE_OPEN_RE)
+ @pre << ret
+ [:CODE_OPEN, ret]
+ elsif ret = @src.scan(CODE_CLOSE_RE)
+ @pre << ret
+ [:CODE_CLOSE, ret]
+ elsif ret = @src.scan(VAR_OPEN_RE)
+ @pre << ret
+ [:VAR_OPEN, ret]
+ elsif ret = @src.scan(VAR_CLOSE_RE)
+ @pre << ret
+ [:VAR_CLOSE, ret]
+ elsif ret = @src.scan(KBD_OPEN_RE)
+ @pre << ret
+ [:KBD_OPEN, ret]
+ elsif ret = @src.scan(KBD_CLOSE_RE)
+ @pre << ret
+ [:KBD_CLOSE, ret]
+ elsif ret = @src.scan(INDEX_OPEN_RE)
+ @pre << ret
+ [:INDEX_OPEN, ret]
+ elsif ret = @src.scan(INDEX_CLOSE_RE)
+ @pre << ret
+ [:INDEX_CLOSE, ret]
+ elsif ret = @src.scan(REF_OPEN_RE)
+ @pre << ret
+ [:REF_OPEN, ret]
+ elsif ret = @src.scan(REF_CLOSE_RE)
+ @pre << ret
+ [:REF_CLOSE, ret]
+ elsif ret = @src.scan(FOOTNOTE_OPEN_RE)
+ @pre << ret
+ [:FOOTNOTE_OPEN, ret]
+ elsif ret = @src.scan(FOOTNOTE_CLOSE_RE)
+ @pre << ret
+ [:FOOTNOTE_CLOSE, ret]
+ elsif ret = @src.scan(VERB_OPEN_RE)
+ @pre << ret
+ [:VERB_OPEN, ret]
+ elsif ret = @src.scan(VERB_CLOSE_RE)
+ @pre << ret
+ [:VERB_CLOSE, ret]
+ elsif ret = @src.scan(BAR_RE)
+ @pre << ret
+ [:BAR, ret]
+ elsif ret = @src.scan(QUOTE_RE)
+ @pre << ret
+ [:QUOTE, ret]
+ elsif ret = @src.scan(SLASH_RE)
+ @pre << ret
+ [:SLASH, ret]
+ elsif ret = @src.scan(BACK_SLASH_RE)
+ @pre << ret
+ [:BACK_SLASH, ret]
+ elsif ret = @src.scan(URL_RE)
+ @pre << ret
+ [:URL, ret]
+ elsif ret = @src.scan(OTHER_RE)
+ @pre << ret
+ [:OTHER, ret]
+ else
+ ret = @src.rest
+ @pre << ret
+ @src.terminate
+ [:OTHER, ret]
+ end
+end
+
+##
+# Raises a ParseError when invalid formatting is found
+
+def on_error(et, ev, values)
+ lines_of_rest = @src.rest.lines.to_a.length
+ prev_words = prev_words_on_error(ev)
+ at = 4 + prev_words.length
+
+ message = <<-MSG
+RD syntax error: line #{@block_parser.line_index - lines_of_rest}:
+...#{prev_words} #{(ev||'')} #{next_words_on_error()} ...
+ MSG
+
+ message << " " * at + "^" * (ev ? ev.length : 0) + "\n"
+ raise ParseError, message
+end
+
+##
+# Returns words before the error
+
+def prev_words_on_error(ev)
+ pre = @pre
+ if ev and /#{Regexp.quote(ev)}$/ =~ pre
+ pre = $`
+ end
+ last_line(pre)
+end
+
+##
+# Returns the last line of +src+
+
+def last_line(src)
+ if n = src.rindex("\n")
+ src[(n+1) .. -1]
+ else
+ src
+ end
+end
+private :last_line
+
+##
+# Returns words following an error
+
+def next_words_on_error
+ if n = @src.rest.index("\n")
+ @src.rest[0 .. (n-1)]
+ else
+ @src.rest
+ end
+end
+
+##
+# Creates a new RDoc::RD::Inline for the +rdoc+ markup and the raw +reference+
+
+def inline rdoc, reference = rdoc
+ RDoc::RD::Inline.new rdoc, reference
+end
+
+# :stopdoc:
+##### State transition tables begin ###
+
+racc_action_table = [
+ 104, 103, 102, 100, 101, 99, 115, 116, 117, 86,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 164, 118, 119, 104, 103, 102, 100, 101, 99, 115,
+ 116, 117, 175, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 85, 118, 119, 63, 64, 65, 61,
+ 81, 62, 76, 78, 79, 84, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 77, 80, 149, 104,
+ 103, 102, 100, 101, 99, 115, 116, 117, 29, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 173,
+ 118, 119, 104, 103, 102, 100, 101, 99, 115, 116,
+ 117, 137, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 177, 118, 119, 63, 64, 65, 153, 81,
+ 62, 76, 78, 79, 148, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 77, 80, 152, 22, 23,
+ 24, 25, 26, 21, 18, 19, 176, 177, 13, 124,
+ 14, 96, 15, 89, 16, 154, 17, 88, 137, 20,
+ 22, 23, 24, 25, 26, 21, 18, 19, 87, 161,
+ 13, nil, 14, nil, 15, nil, 16, nil, 17, 42,
+ nil, 20, 54, 38, 53, 55, 56, 57, nil, 13,
+ nil, 14, nil, 15, nil, 16, nil, 17, nil, nil,
+ 20, 22, 23, 24, 25, 26, 21, 18, 19, nil,
+ nil, 13, nil, 14, nil, 15, nil, 16, nil, 17,
+ nil, nil, 20, 63, 64, 65, 61, 81, 62, 76,
+ 78, 79, nil, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 77, 80, 145, nil, nil, 54, 133,
+ 53, 55, 56, 57, nil, 13, nil, 14, nil, 15,
+ nil, 16, nil, 17, 145, nil, 20, 54, 133, 53,
+ 55, 56, 57, nil, 13, nil, 14, nil, 15, nil,
+ 16, nil, 17, nil, nil, 20, 22, 23, 24, 25,
+ 26, 21, 18, 19, nil, nil, 13, nil, 14, nil,
+ 15, nil, 16, nil, 17, 145, nil, 20, 54, 133,
+ 53, 55, 56, 57, nil, 13, nil, 14, nil, 15,
+ nil, 16, nil, 17, 145, nil, 20, 54, 133, 53,
+ 55, 56, 57, nil, 13, nil, 14, nil, 15, nil,
+ 16, nil, 17, nil, nil, 20, 22, 23, 24, 25,
+ 26, 21, 18, 19, nil, nil, 13, nil, 14, nil,
+ 15, nil, 16, nil, 17, nil, nil, 20, 22, 23,
+ 24, 25, 26, 21, 18, 19, nil, nil, 13, nil,
+ 14, nil, 15, nil, 16, nil, 17, nil, nil, 20,
+ 22, 23, 24, 25, 26, 21, 18, 19, nil, nil,
+ 13, nil, 14, nil, 15, nil, 16, nil, 17, nil,
+ nil, 20, 22, 23, 24, 25, 26, 21, 18, 19,
+ nil, nil, 13, nil, 14, nil, 15, nil, 16, 122,
+ 17, nil, 54, 20, 53, 55, 56, 57, nil, 13,
+ nil, 14, nil, 15, nil, 16, nil, 17, nil, nil,
+ 20, 135, 136, 54, 133, 53, 55, 56, 57, nil,
+ 13, nil, 14, nil, 15, nil, 16, nil, 17, nil,
+ nil, 20, 135, 136, 54, 133, 53, 55, 56, 57,
+ nil, 13, nil, 14, nil, 15, nil, 16, nil, 17,
+ nil, nil, 20, 135, 136, 54, 133, 53, 55, 56,
+ 57, nil, 13, nil, 14, nil, 15, nil, 16, 158,
+ 17, nil, 54, 20, 53, 55, 56, 57, 95, nil,
+ nil, 54, 91, 53, 55, 56, 57, 145, nil, nil,
+ 54, 133, 53, 55, 56, 57, 165, 135, 136, 54,
+ 133, 53, 55, 56, 57, 145, nil, nil, 54, 133,
+ 53, 55, 56, 57, 172, 135, 136, 54, 133, 53,
+ 55, 56, 57, 174, 135, 136, 54, 133, 53, 55,
+ 56, 57, 178, 135, 136, 54, 133, 53, 55, 56,
+ 57, 135, 136, 54, 133, 53, 55, 56, 57, 135,
+ 136, 54, 133, 53, 55, 56, 57, 135, 136, 54,
+ 133, 53, 55, 56, 57, 22, 23, 24, 25, 26,
+ 21 ]
+
+racc_action_check = [
+ 38, 38, 38, 38, 38, 38, 38, 38, 38, 32,
+ 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+ 125, 38, 38, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 164, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 31, 97, 97, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 29, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 1, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 162,
+ 91, 91, 155, 155, 155, 155, 155, 155, 155, 155,
+ 155, 43, 155, 155, 155, 155, 155, 155, 155, 155,
+ 155, 155, 172, 155, 155, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 58, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 16, 16,
+ 16, 16, 16, 16, 16, 16, 165, 165, 16, 41,
+ 16, 37, 16, 35, 16, 90, 16, 34, 94, 16,
+ 17, 17, 17, 17, 17, 17, 17, 17, 33, 100,
+ 17, nil, 17, nil, 17, nil, 17, nil, 17, 18,
+ nil, 17, 18, 18, 18, 18, 18, 18, nil, 18,
+ nil, 18, nil, 18, nil, 18, nil, 18, nil, nil,
+ 18, 19, 19, 19, 19, 19, 19, 19, 19, nil,
+ nil, 19, nil, 19, nil, 19, nil, 19, nil, 19,
+ nil, nil, 19, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, nil, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 146, nil, nil, 146, 146,
+ 146, 146, 146, 146, nil, 146, nil, 146, nil, 146,
+ nil, 146, nil, 146, 138, nil, 146, 138, 138, 138,
+ 138, 138, 138, nil, 138, nil, 138, nil, 138, nil,
+ 138, nil, 138, nil, nil, 138, 0, 0, 0, 0,
+ 0, 0, 0, 0, nil, nil, 0, nil, 0, nil,
+ 0, nil, 0, nil, 0, 45, nil, 0, 45, 45,
+ 45, 45, 45, 45, nil, 45, nil, 45, nil, 45,
+ nil, 45, nil, 45, 44, nil, 45, 44, 44, 44,
+ 44, 44, 44, nil, 44, nil, 44, nil, 44, nil,
+ 44, nil, 44, nil, nil, 44, 2, 2, 2, 2,
+ 2, 2, 2, 2, nil, nil, 2, nil, 2, nil,
+ 2, nil, 2, nil, 2, nil, nil, 2, 13, 13,
+ 13, 13, 13, 13, 13, 13, nil, nil, 13, nil,
+ 13, nil, 13, nil, 13, nil, 13, nil, nil, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14, nil, nil,
+ 14, nil, 14, nil, 14, nil, 14, nil, 14, nil,
+ nil, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+ nil, nil, 15, nil, 15, nil, 15, nil, 15, 39,
+ 15, nil, 39, 15, 39, 39, 39, 39, nil, 39,
+ nil, 39, nil, 39, nil, 39, nil, 39, nil, nil,
+ 39, 42, 42, 42, 42, 42, 42, 42, 42, nil,
+ 42, nil, 42, nil, 42, nil, 42, nil, 42, nil,
+ nil, 42, 127, 127, 127, 127, 127, 127, 127, 127,
+ nil, 127, nil, 127, nil, 127, nil, 127, nil, 127,
+ nil, nil, 127, 122, 122, 122, 122, 122, 122, 122,
+ 122, nil, 122, nil, 122, nil, 122, nil, 122, 92,
+ 122, nil, 92, 122, 92, 92, 92, 92, 36, nil,
+ nil, 36, 36, 36, 36, 36, 36, 52, nil, nil,
+ 52, 52, 52, 52, 52, 52, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 142, nil, nil, 142, 142,
+ 142, 142, 142, 142, 159, 159, 159, 159, 159, 159,
+ 159, 159, 159, 163, 163, 163, 163, 163, 163, 163,
+ 163, 163, 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 95, 95, 95, 95, 95, 95, 95, 95, 158,
+ 158, 158, 158, 158, 158, 158, 158, 168, 168, 168,
+ 168, 168, 168, 168, 168, 27, 27, 27, 27, 27,
+ 27 ]
+
+racc_action_pointer = [
+ 283, 78, 343, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, 365, 387, 409, 135, 157, 176, 198,
+ 220, nil, nil, nil, nil, nil, nil, 602, nil, 55,
+ nil, 29, -7, 150, 137, 131, 515, 128, -3, 426,
+ nil, 145, 447, 96, 321, 302, nil, nil, nil, nil,
+ nil, nil, 524, nil, nil, nil, nil, nil, 113, 43,
+ nil, 112, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 132, 66, 506, nil, 153, 577, nil, 20, nil, nil,
+ 163, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, 489, nil, nil, 17, 533, 468, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 261, nil,
+ nil, nil, 542, nil, nil, nil, 242, nil, nil, nil,
+ nil, nil, nil, nil, nil, 89, nil, nil, 585, 551,
+ nil, nil, 86, 560, 28, 142, nil, nil, 593, nil,
+ nil, 569, 107, nil, nil, nil, nil, nil, nil ]
+
+racc_action_default = [
+ -138, -138, -1, -3, -4, -5, -6, -7, -8, -9,
+ -10, -11, -12, -138, -138, -138, -138, -138, -138, -138,
+ -138, -103, -104, -105, -106, -107, -108, -111, -110, -138,
+ -2, -138, -138, -138, -138, -138, -138, -138, -138, -27,
+ -26, -35, -138, -58, -41, -40, -47, -48, -49, -50,
+ -51, -52, -63, -66, -67, -68, -69, -70, -138, -138,
+ -112, -138, -116, -117, -118, -119, -120, -121, -122, -123,
+ -124, -125, -126, -127, -128, -129, -130, -131, -132, -133,
+ -134, -135, -137, -109, 179, -13, -14, -15, -16, -17,
+ -138, -138, -23, -22, -33, -138, -19, -24, -79, -80,
+ -138, -82, -83, -84, -85, -86, -87, -88, -89, -90,
+ -91, -92, -93, -94, -95, -96, -97, -98, -99, -100,
+ -25, -35, -138, -58, -28, -138, -59, -42, -46, -55,
+ -56, -65, -71, -72, -75, -76, -77, -31, -38, -44,
+ -53, -54, -57, -61, -73, -74, -39, -62, -101, -102,
+ -136, -113, -114, -115, -18, -20, -21, -33, -138, -138,
+ -78, -81, -138, -59, -36, -37, -64, -45, -59, -43,
+ -60, -138, -34, -36, -37, -29, -30, -32, -34 ]
+
+racc_goto_table = [
+ 126, 44, 125, 52, 144, 144, 160, 93, 97, 43,
+ 166, 82, 144, 41, 40, 39, 138, 146, 169, 90,
+ 36, 52, 44, 1, 52, 129, 169, 94, 59, 83,
+ 123, 30, 151, 92, 121, 120, 31, 32, 33, 34,
+ 35, 170, 58, 166, 167, 147, 170, 166, 37, nil,
+ 150, nil, 166, 159, 4, 166, 4, nil, nil, nil,
+ nil, 155, nil, 156, 160, nil, nil, 4, 4, 4,
+ 4, 4, nil, 4, 5, nil, 5, 52, nil, nil,
+ 163, nil, 162, 157, nil, 168, nil, 5, 5, 5,
+ 5, 5, nil, 5, nil, nil, nil, nil, 144, nil,
+ nil, nil, 144, nil, nil, 129, 144, 144, nil, 6,
+ 129, 6, nil, nil, nil, nil, 171, 7, nil, 7,
+ nil, nil, 6, 6, 6, 6, 6, 8, 6, 8,
+ 7, 7, 7, 7, 7, 11, 7, 11, nil, nil,
+ 8, 8, 8, 8, 8, nil, 8, nil, 11, 11,
+ 11, 11, 11, nil, 11 ]
+
+racc_goto_check = [
+ 22, 24, 21, 34, 36, 36, 37, 18, 16, 23,
+ 35, 41, 36, 20, 19, 17, 25, 25, 28, 14,
+ 13, 34, 24, 1, 34, 24, 28, 23, 38, 39,
+ 23, 3, 42, 17, 20, 19, 1, 1, 1, 1,
+ 1, 33, 1, 35, 29, 32, 33, 35, 15, nil,
+ 41, nil, 35, 22, 4, 35, 4, nil, nil, nil,
+ nil, 16, nil, 18, 37, nil, nil, 4, 4, 4,
+ 4, 4, nil, 4, 5, nil, 5, 34, nil, nil,
+ 22, nil, 21, 23, nil, 22, nil, 5, 5, 5,
+ 5, 5, nil, 5, nil, nil, nil, nil, 36, nil,
+ nil, nil, 36, nil, nil, 24, 36, 36, nil, 6,
+ 24, 6, nil, nil, nil, nil, 22, 7, nil, 7,
+ nil, nil, 6, 6, 6, 6, 6, 8, 6, 8,
+ 7, 7, 7, 7, 7, 11, 7, 11, nil, nil,
+ 8, 8, 8, 8, 8, nil, 8, nil, 11, 11,
+ 11, 11, 11, nil, 11 ]
+
+racc_goto_pointer = [
+ nil, 23, nil, 29, 54, 74, 109, 117, 127, nil,
+ nil, 135, nil, 2, -17, 30, -30, -3, -29, -4,
+ -5, -40, -42, -9, -17, -28, nil, nil, -120, -83,
+ nil, nil, -7, -101, -15, -116, -40, -91, 8, 2,
+ nil, -9, -29 ]
+
+racc_goto_default = [
+ nil, nil, 2, 3, 46, 47, 48, 49, 50, 9,
+ 10, 51, 12, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, 140, nil, 45, 127, 139, 128,
+ 141, 130, 142, 143, 132, 131, 134, 98, nil, 28,
+ 27, nil, 60 ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 27, :_reduce_none,
+ 2, 28, :_reduce_2,
+ 1, 28, :_reduce_3,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 1, 29, :_reduce_none,
+ 3, 30, :_reduce_13,
+ 3, 31, :_reduce_14,
+ 3, 32, :_reduce_15,
+ 3, 33, :_reduce_16,
+ 3, 34, :_reduce_17,
+ 4, 35, :_reduce_18,
+ 3, 35, :_reduce_19,
+ 2, 40, :_reduce_20,
+ 2, 40, :_reduce_21,
+ 1, 40, :_reduce_22,
+ 1, 40, :_reduce_23,
+ 2, 41, :_reduce_24,
+ 2, 41, :_reduce_25,
+ 1, 41, :_reduce_26,
+ 1, 41, :_reduce_27,
+ 2, 39, :_reduce_none,
+ 4, 39, :_reduce_29,
+ 4, 39, :_reduce_30,
+ 2, 43, :_reduce_31,
+ 4, 43, :_reduce_32,
+ 1, 44, :_reduce_33,
+ 3, 44, :_reduce_34,
+ 1, 45, :_reduce_none,
+ 3, 45, :_reduce_36,
+ 3, 45, :_reduce_37,
+ 2, 46, :_reduce_38,
+ 2, 46, :_reduce_39,
+ 1, 46, :_reduce_40,
+ 1, 46, :_reduce_41,
+ 1, 47, :_reduce_none,
+ 2, 51, :_reduce_43,
+ 1, 51, :_reduce_44,
+ 2, 53, :_reduce_45,
+ 1, 53, :_reduce_46,
+ 1, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 56, :_reduce_57,
+ 1, 52, :_reduce_58,
+ 1, 57, :_reduce_59,
+ 2, 58, :_reduce_60,
+ 1, 58, :_reduce_none,
+ 2, 49, :_reduce_62,
+ 1, 49, :_reduce_none,
+ 2, 48, :_reduce_64,
+ 1, 48, :_reduce_none,
+ 1, 60, :_reduce_none,
+ 1, 60, :_reduce_none,
+ 1, 60, :_reduce_none,
+ 1, 60, :_reduce_none,
+ 1, 60, :_reduce_none,
+ 1, 62, :_reduce_none,
+ 1, 62, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 61, :_reduce_none,
+ 1, 61, :_reduce_none,
+ 1, 61, :_reduce_none,
+ 2, 42, :_reduce_78,
+ 1, 42, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 2, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 1, 63, :_reduce_none,
+ 3, 36, :_reduce_101,
+ 3, 37, :_reduce_102,
+ 1, 65, :_reduce_none,
+ 1, 65, :_reduce_none,
+ 1, 65, :_reduce_none,
+ 1, 65, :_reduce_none,
+ 1, 65, :_reduce_none,
+ 1, 65, :_reduce_none,
+ 2, 66, :_reduce_109,
+ 1, 66, :_reduce_none,
+ 1, 38, :_reduce_111,
+ 1, 67, :_reduce_none,
+ 2, 67, :_reduce_113,
+ 2, 67, :_reduce_114,
+ 2, 67, :_reduce_115,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 2, 64, :_reduce_136,
+ 1, 64, :_reduce_none ]
+
+racc_reduce_n = 138
+
+racc_shift_n = 179
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ :EX_LOW => 2,
+ :QUOTE => 3,
+ :BAR => 4,
+ :SLASH => 5,
+ :BACK_SLASH => 6,
+ :URL => 7,
+ :OTHER => 8,
+ :REF_OPEN => 9,
+ :FOOTNOTE_OPEN => 10,
+ :FOOTNOTE_CLOSE => 11,
+ :EX_HIGH => 12,
+ :EM_OPEN => 13,
+ :EM_CLOSE => 14,
+ :CODE_OPEN => 15,
+ :CODE_CLOSE => 16,
+ :VAR_OPEN => 17,
+ :VAR_CLOSE => 18,
+ :KBD_OPEN => 19,
+ :KBD_CLOSE => 20,
+ :INDEX_OPEN => 21,
+ :INDEX_CLOSE => 22,
+ :REF_CLOSE => 23,
+ :VERB_OPEN => 24,
+ :VERB_CLOSE => 25 }
+
+racc_nt_base = 26
+
+racc_use_result_var = true
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "EX_LOW",
+ "QUOTE",
+ "BAR",
+ "SLASH",
+ "BACK_SLASH",
+ "URL",
+ "OTHER",
+ "REF_OPEN",
+ "FOOTNOTE_OPEN",
+ "FOOTNOTE_CLOSE",
+ "EX_HIGH",
+ "EM_OPEN",
+ "EM_CLOSE",
+ "CODE_OPEN",
+ "CODE_CLOSE",
+ "VAR_OPEN",
+ "VAR_CLOSE",
+ "KBD_OPEN",
+ "KBD_CLOSE",
+ "INDEX_OPEN",
+ "INDEX_CLOSE",
+ "REF_CLOSE",
+ "VERB_OPEN",
+ "VERB_CLOSE",
+ "$start",
+ "content",
+ "elements",
+ "element",
+ "emphasis",
+ "code",
+ "var",
+ "keyboard",
+ "index",
+ "reference",
+ "footnote",
+ "verb",
+ "normal_str_ele",
+ "substitute",
+ "ref_label",
+ "ref_label2",
+ "ref_url_strings",
+ "filename",
+ "element_label",
+ "element_label2",
+ "ref_subst_content",
+ "ref_subst_content_q",
+ "ref_subst_strings_q",
+ "ref_subst_strings_first",
+ "ref_subst_ele2",
+ "ref_subst_eles",
+ "ref_subst_str_ele_first",
+ "ref_subst_eles_q",
+ "ref_subst_ele",
+ "ref_subst_ele_q",
+ "ref_subst_str_ele",
+ "ref_subst_str_ele_q",
+ "ref_subst_strings",
+ "ref_subst_string3",
+ "ref_subst_string",
+ "ref_subst_string_q",
+ "ref_subst_string2",
+ "ref_url_string",
+ "verb_strings",
+ "normal_string",
+ "normal_strings",
+ "verb_string",
+ "verb_normal_string" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+# reduce 1 omitted
+
+def _reduce_2(val, _values, result)
+ result.append val[1]
+ result
+end
+
+def _reduce_3(val, _values, result)
+ result = val[0]
+ result
+end
+
+# reduce 4 omitted
+
+# reduce 5 omitted
+
+# reduce 6 omitted
+
+# reduce 7 omitted
+
+# reduce 8 omitted
+
+# reduce 9 omitted
+
+# reduce 10 omitted
+
+# reduce 11 omitted
+
+# reduce 12 omitted
+
+def _reduce_13(val, _values, result)
+ content = val[1]
+ result = inline "<em>#{content}</em>", content
+
+ result
+end
+
+def _reduce_14(val, _values, result)
+ content = val[1]
+ result = inline "<code>#{content}</code>", content
+
+ result
+end
+
+def _reduce_15(val, _values, result)
+ content = val[1]
+ result = inline "+#{content}+", content
+
+ result
+end
+
+def _reduce_16(val, _values, result)
+ content = val[1]
+ result = inline "<tt>#{content}</tt>", content
+
+ result
+end
+
+def _reduce_17(val, _values, result)
+ label = val[1]
+ @block_parser.add_label label.reference
+ result = "<span id=\"label-#{label}\">#{label}</span>"
+
+ result
+end
+
+def _reduce_18(val, _values, result)
+ result = "{#{val[1]}}[#{val[2].join}]"
+
+ result
+end
+
+def _reduce_19(val, _values, result)
+ scheme, inline = val[1]
+
+ result = "{#{inline}}[#{scheme}#{inline.reference}]"
+
+ result
+end
+
+def _reduce_20(val, _values, result)
+ result = [nil, inline(val[1])]
+
+ result
+end
+
+def _reduce_21(val, _values, result)
+ result = [
+ 'rdoc-label:',
+ inline("#{val[0].reference}/#{val[1].reference}")
+ ]
+
+ result
+end
+
+def _reduce_22(val, _values, result)
+ result = ['rdoc-label:', val[0].reference]
+
+ result
+end
+
+def _reduce_23(val, _values, result)
+ result = ['rdoc-label:', "#{val[0].reference}/"]
+
+ result
+end
+
+def _reduce_24(val, _values, result)
+ result = [nil, inline(val[1])]
+
+ result
+end
+
+def _reduce_25(val, _values, result)
+ result = [
+ 'rdoc-label:',
+ inline("#{val[0].reference}/#{val[1].reference}")
+ ]
+
+ result
+end
+
+def _reduce_26(val, _values, result)
+ result = ['rdoc-label:', val[0]]
+
+ result
+end
+
+def _reduce_27(val, _values, result)
+ ref = val[0].reference
+ result = ['rdoc-label:', inline(ref, "#{ref}/")]
+
+ result
+end
+
+# reduce 28 omitted
+
+def _reduce_29(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_30(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_31(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+def _reduce_32(val, _values, result)
+ result = inline "\"#{val[1]}\""
+
+ result
+end
+
+def _reduce_33(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+def _reduce_34(val, _values, result)
+ result = inline "\"#{val[1]}\""
+
+ result
+end
+
+# reduce 35 omitted
+
+def _reduce_36(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_37(val, _values, result)
+ result = inline val[1]
+ result
+end
+
+def _reduce_38(val, _values, result)
+ result = val[0].append val[1]
+
+ result
+end
+
+def _reduce_39(val, _values, result)
+ result = val[0].append val[1]
+
+ result
+end
+
+def _reduce_40(val, _values, result)
+ result = val[0]
+
+ result
+end
+
+def _reduce_41(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+# reduce 42 omitted
+
+def _reduce_43(val, _values, result)
+ result = val[0].append val[1]
+
+ result
+end
+
+def _reduce_44(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+def _reduce_45(val, _values, result)
+ result = val[0].append val[1]
+
+ result
+end
+
+def _reduce_46(val, _values, result)
+ result = val[0]
+
+ result
+end
+
+# reduce 47 omitted
+
+# reduce 48 omitted
+
+# reduce 49 omitted
+
+# reduce 50 omitted
+
+# reduce 51 omitted
+
+# reduce 52 omitted
+
+# reduce 53 omitted
+
+# reduce 54 omitted
+
+# reduce 55 omitted
+
+# reduce 56 omitted
+
+def _reduce_57(val, _values, result)
+ result = val[0]
+
+ result
+end
+
+def _reduce_58(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+def _reduce_59(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+def _reduce_60(val, _values, result)
+ result << val[1]
+ result
+end
+
+# reduce 61 omitted
+
+def _reduce_62(val, _values, result)
+ result << val[1]
+
+ result
+end
+
+# reduce 63 omitted
+
+def _reduce_64(val, _values, result)
+ result << val[1]
+
+ result
+end
+
+# reduce 65 omitted
+
+# reduce 66 omitted
+
+# reduce 67 omitted
+
+# reduce 68 omitted
+
+# reduce 69 omitted
+
+# reduce 70 omitted
+
+# reduce 71 omitted
+
+# reduce 72 omitted
+
+# reduce 73 omitted
+
+# reduce 74 omitted
+
+# reduce 75 omitted
+
+# reduce 76 omitted
+
+# reduce 77 omitted
+
+def _reduce_78(val, _values, result)
+ result << val[1]
+ result
+end
+
+# reduce 79 omitted
+
+# reduce 80 omitted
+
+# reduce 81 omitted
+
+# reduce 82 omitted
+
+# reduce 83 omitted
+
+# reduce 84 omitted
+
+# reduce 85 omitted
+
+# reduce 86 omitted
+
+# reduce 87 omitted
+
+# reduce 88 omitted
+
+# reduce 89 omitted
+
+# reduce 90 omitted
+
+# reduce 91 omitted
+
+# reduce 92 omitted
+
+# reduce 93 omitted
+
+# reduce 94 omitted
+
+# reduce 95 omitted
+
+# reduce 96 omitted
+
+# reduce 97 omitted
+
+# reduce 98 omitted
+
+# reduce 99 omitted
+
+# reduce 100 omitted
+
+def _reduce_101(val, _values, result)
+ index = @block_parser.add_footnote val[1].rdoc
+ result = "{*#{index}}[rdoc-label:foottext-#{index}:footmark-#{index}]"
+
+ result
+end
+
+def _reduce_102(val, _values, result)
+ result = inline "<tt>#{val[1]}</tt>", val[1]
+
+ result
+end
+
+# reduce 103 omitted
+
+# reduce 104 omitted
+
+# reduce 105 omitted
+
+# reduce 106 omitted
+
+# reduce 107 omitted
+
+# reduce 108 omitted
+
+def _reduce_109(val, _values, result)
+ result << val[1]
+ result
+end
+
+# reduce 110 omitted
+
+def _reduce_111(val, _values, result)
+ result = inline val[0]
+
+ result
+end
+
+# reduce 112 omitted
+
+def _reduce_113(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_114(val, _values, result)
+ result = val[1]
+ result
+end
+
+def _reduce_115(val, _values, result)
+ result = val[1]
+ result
+end
+
+# reduce 116 omitted
+
+# reduce 117 omitted
+
+# reduce 118 omitted
+
+# reduce 119 omitted
+
+# reduce 120 omitted
+
+# reduce 121 omitted
+
+# reduce 122 omitted
+
+# reduce 123 omitted
+
+# reduce 124 omitted
+
+# reduce 125 omitted
+
+# reduce 126 omitted
+
+# reduce 127 omitted
+
+# reduce 128 omitted
+
+# reduce 129 omitted
+
+# reduce 130 omitted
+
+# reduce 131 omitted
+
+# reduce 132 omitted
+
+# reduce 133 omitted
+
+# reduce 134 omitted
+
+# reduce 135 omitted
+
+def _reduce_136(val, _values, result)
+ result << val[1]
+ result
+end
+
+# reduce 137 omitted
+
+def _reduce_none(val, _values, result)
+ val[0]
+end
+
+end # class InlineParser
+
+end
diff --git a/lib/rdoc/rdoc.gemspec b/lib/rdoc/rdoc.gemspec
new file mode 100644
index 0000000000..8c92908a66
--- /dev/null
+++ b/lib/rdoc/rdoc.gemspec
@@ -0,0 +1,63 @@
+begin
+ require_relative "lib/rdoc"
+rescue LoadError
+ # for Ruby repository
+ require_relative "../rdoc"
+end
+
+Gem::Specification.new do |s|
+ s.name = "rdoc"
+ s.version = RDoc::VERSION
+ s.date = "2017-12-24"
+
+ s.authors = [
+ "Eric Hodel",
+ "Dave Thomas",
+ "Phil Hagelberg",
+ "Tony Strauss",
+ "Zachary Scott",
+ "Hiroshi SHIBATA",
+ "ITOYANAGI Sakura"
+ ]
+ s.email = ["drbrain@segment7.net", "", "", "", "mail@zzak.io", "hsbt@ruby-lang.org", "aycabta@gmail.com"]
+
+ s.summary = "RDoc produces HTML and command-line documentation for Ruby projects"
+ s.description = <<-DESCRIPTION
+RDoc produces HTML and command-line documentation for Ruby projects.
+RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentation from the command-line.
+ DESCRIPTION
+ s.homepage = "https://ruby.github.io/rdoc"
+ s.licenses = ["Ruby"]
+
+ s.bindir = "exe"
+ s.executables = ["rdoc", "ri"]
+ s.require_paths = ["lib"]
+ # for ruby core repository. It was generated by `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ s.files = [".document", ".gitignore", ".travis.yml", "CONTRIBUTING.rdoc", "CVE-2013-0256.rdoc", "ExampleMarkdown.md", "ExampleRDoc.rdoc", "Gemfile", "History.rdoc", "LEGAL.rdoc", "LICENSE.rdoc", "README.rdoc", "RI.rdoc", "Rakefile", "TODO.rdoc", "appveyor.yml", "bin/console", "bin/setup", "exe/rdoc", "exe/ri", "lib/rdoc.rb", "lib/rdoc/alias.rb", "lib/rdoc/anon_class.rb", "lib/rdoc/any_method.rb", "lib/rdoc/attr.rb", "lib/rdoc/class_module.rb", "lib/rdoc/code_object.rb", "lib/rdoc/code_objects.rb", "lib/rdoc/comment.rb", "lib/rdoc/constant.rb", "lib/rdoc/context.rb", "lib/rdoc/context/section.rb", "lib/rdoc/cross_reference.rb", "lib/rdoc/encoding.rb", "lib/rdoc/erb_partial.rb", "lib/rdoc/erbio.rb", "lib/rdoc/extend.rb", "lib/rdoc/generator.rb", "lib/rdoc/generator/darkfish.rb", "lib/rdoc/generator/json_index.rb", "lib/rdoc/generator/markup.rb", "lib/rdoc/generator/pot.rb", "lib/rdoc/generator/pot/message_extractor.rb", "lib/rdoc/generator/pot/po.rb", "lib/rdoc/generator/pot/po_entry.rb", "lib/rdoc/generator/ri.rb", "lib/rdoc/generator/template/darkfish/.document", "lib/rdoc/generator/template/darkfish/_footer.rhtml", "lib/rdoc/generator/template/darkfish/_head.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml", "lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml", "lib/rdoc/generator/template/darkfish/class.rhtml", "lib/rdoc/generator/template/darkfish/css/fonts.css", "lib/rdoc/generator/template/darkfish/css/rdoc.css", "lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf", "lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf", "lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf", "lib/rdoc/generator/template/darkfish/images/add.png", "lib/rdoc/generator/template/darkfish/images/arrow_up.png", "lib/rdoc/generator/template/darkfish/images/brick.png", "lib/rdoc/generator/template/darkfish/images/brick_link.png", "lib/rdoc/generator/template/darkfish/images/bug.png", "lib/rdoc/generator/template/darkfish/images/bullet_black.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png", "lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png", "lib/rdoc/generator/template/darkfish/images/date.png", "lib/rdoc/generator/template/darkfish/images/delete.png", "lib/rdoc/generator/template/darkfish/images/find.png", "lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif", "lib/rdoc/generator/template/darkfish/images/macFFBgHack.png", "lib/rdoc/generator/template/darkfish/images/package.png", "lib/rdoc/generator/template/darkfish/images/page_green.png", "lib/rdoc/generator/template/darkfish/images/page_white_text.png", "lib/rdoc/generator/template/darkfish/images/page_white_width.png", "lib/rdoc/generator/template/darkfish/images/plugin.png", "lib/rdoc/generator/template/darkfish/images/ruby.png", "lib/rdoc/generator/template/darkfish/images/tag_blue.png", "lib/rdoc/generator/template/darkfish/images/tag_green.png", "lib/rdoc/generator/template/darkfish/images/transparent.png", "lib/rdoc/generator/template/darkfish/images/wrench.png", "lib/rdoc/generator/template/darkfish/images/wrench_orange.png", "lib/rdoc/generator/template/darkfish/images/zoom.png", "lib/rdoc/generator/template/darkfish/index.rhtml", "lib/rdoc/generator/template/darkfish/js/darkfish.js", "lib/rdoc/generator/template/darkfish/js/jquery.js", "lib/rdoc/generator/template/darkfish/js/search.js", "lib/rdoc/generator/template/darkfish/page.rhtml", "lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml", "lib/rdoc/generator/template/darkfish/servlet_root.rhtml", "lib/rdoc/generator/template/darkfish/table_of_contents.rhtml", "lib/rdoc/generator/template/json_index/.document", "lib/rdoc/generator/template/json_index/js/navigation.js", "lib/rdoc/generator/template/json_index/js/searcher.js", "lib/rdoc/ghost_method.rb", "lib/rdoc/i18n.rb", "lib/rdoc/i18n/locale.rb", "lib/rdoc/i18n/text.rb", "lib/rdoc/include.rb", "lib/rdoc/known_classes.rb", "lib/rdoc/markdown.kpeg", "lib/rdoc/markdown/entities.rb", "lib/rdoc/markdown/literals.kpeg", "lib/rdoc/markup.rb", "lib/rdoc/markup/attr_changer.rb", "lib/rdoc/markup/attr_span.rb", "lib/rdoc/markup/attribute_manager.rb", "lib/rdoc/markup/attributes.rb", "lib/rdoc/markup/blank_line.rb", "lib/rdoc/markup/block_quote.rb", "lib/rdoc/markup/document.rb", "lib/rdoc/markup/formatter.rb", "lib/rdoc/markup/formatter_test_case.rb", "lib/rdoc/markup/hard_break.rb", "lib/rdoc/markup/heading.rb", "lib/rdoc/markup/include.rb", "lib/rdoc/markup/indented_paragraph.rb", "lib/rdoc/markup/inline.rb", "lib/rdoc/markup/list.rb", "lib/rdoc/markup/list_item.rb", "lib/rdoc/markup/paragraph.rb", "lib/rdoc/markup/parser.rb", "lib/rdoc/markup/pre_process.rb", "lib/rdoc/markup/raw.rb", "lib/rdoc/markup/rule.rb", "lib/rdoc/markup/special.rb", "lib/rdoc/markup/text_formatter_test_case.rb", "lib/rdoc/markup/to_ansi.rb", "lib/rdoc/markup/to_bs.rb", "lib/rdoc/markup/to_html.rb", "lib/rdoc/markup/to_html_crossref.rb", "lib/rdoc/markup/to_html_snippet.rb", "lib/rdoc/markup/to_joined_paragraph.rb", "lib/rdoc/markup/to_label.rb", "lib/rdoc/markup/to_markdown.rb", "lib/rdoc/markup/to_rdoc.rb", "lib/rdoc/markup/to_table_of_contents.rb", "lib/rdoc/markup/to_test.rb", "lib/rdoc/markup/to_tt_only.rb", "lib/rdoc/markup/verbatim.rb", "lib/rdoc/meta_method.rb", "lib/rdoc/method_attr.rb", "lib/rdoc/mixin.rb", "lib/rdoc/normal_class.rb", "lib/rdoc/normal_module.rb", "lib/rdoc/options.rb", "lib/rdoc/parser.rb", "lib/rdoc/parser/c.rb", "lib/rdoc/parser/changelog.rb", "lib/rdoc/parser/markdown.rb", "lib/rdoc/parser/rd.rb", "lib/rdoc/parser/ripper_state_lex.rb", "lib/rdoc/parser/ruby.rb", "lib/rdoc/parser/ruby_tools.rb", "lib/rdoc/parser/simple.rb", "lib/rdoc/parser/text.rb", "lib/rdoc/rd.rb", "lib/rdoc/rd/block_parser.ry", "lib/rdoc/rd/inline.rb", "lib/rdoc/rd/inline_parser.ry", "lib/rdoc/rdoc.rb", "lib/rdoc/require.rb", "lib/rdoc/ri.rb", "lib/rdoc/ri/driver.rb", "lib/rdoc/ri/formatter.rb", "lib/rdoc/ri/paths.rb", "lib/rdoc/ri/store.rb", "lib/rdoc/ri/task.rb", "lib/rdoc/rubygems_hook.rb", "lib/rdoc/servlet.rb", "lib/rdoc/single_class.rb", "lib/rdoc/stats.rb", "lib/rdoc/stats/normal.rb", "lib/rdoc/stats/quiet.rb", "lib/rdoc/stats/verbose.rb", "lib/rdoc/store.rb", "lib/rdoc/task.rb", "lib/rdoc/test_case.rb", "lib/rdoc/text.rb", "lib/rdoc/token_stream.rb", "lib/rdoc/tom_doc.rb", "lib/rdoc/top_level.rb", "rdoc.gemspec"]
+ # files from .gitignore
+ s.files << "lib/rdoc/rd/block_parser.rb" << "lib/rdoc/rd/inline_parser.rb" << "lib/rdoc/markdown.rb" << "lib/rdoc/markdown/literals.rb"
+
+ s.rdoc_options = ["--main", "README.rdoc"]
+ s.extra_rdoc_files += %w[
+ CVE-2013-0256.rdoc
+ CONTRIBUTING.rdoc
+ ExampleMarkdown.md
+ ExampleRDoc.rdoc
+ History.rdoc
+ LEGAL.rdoc
+ LICENSE.rdoc
+ README.rdoc
+ RI.rdoc
+ TODO.rdoc
+ ]
+
+ s.required_ruby_version = Gem::Requirement.new(">= 2.2.2")
+ s.rubygems_version = "2.5.2"
+ s.required_rubygems_version = Gem::Requirement.new(">= 2.2")
+
+ s.add_development_dependency("rake")
+ s.add_development_dependency("racc", "> 1.4.10")
+ s.add_development_dependency("kpeg")
+ s.add_development_dependency("minitest", "~> 4")
+ s.add_development_dependency("json")
+end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index cb5d6501d8..68775c8be1 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -1,278 +1,571 @@
-# See README.
+# frozen_string_literal: true
+require 'rdoc'
+
+require 'find'
+require 'fileutils'
+require 'pathname'
+require 'time'
+
+##
+# This is the driver for generating RDoc output. It handles file parsing and
+# generation of output.
+#
+# To use this class to generate RDoc output via the API, the recommended way
+# is:
+#
+# rdoc = RDoc::RDoc.new
+# options = rdoc.load_options # returns an RDoc::Options instance
+# # set extra options
+# rdoc.document options
#
-
+# You can also generate output like the +rdoc+ executable:
+#
+# rdoc = RDoc::RDoc.new
+# rdoc.document argv
+#
+# Where +argv+ is an array of strings, each corresponding to an argument you'd
+# give rdoc on the command line. See <tt>rdoc --help<tt> for details.
-VERSION_STRING = %{RDoc V1.0.1 - 20041108}
+class RDoc::RDoc
+ @current = nil
-require 'rdoc/parsers/parse_rb.rb'
-require 'rdoc/parsers/parse_c.rb'
-require 'rdoc/parsers/parse_f95.rb'
+ ##
+ # This is the list of supported output generators
-require 'rdoc/parsers/parse_simple.rb'
-require 'rdoc/options'
+ GENERATORS = {}
-require 'rdoc/diagram'
+ ##
+ # File pattern to exclude
-require 'find'
-require 'ftools'
+ attr_accessor :exclude
-# We put rdoc stuff in the RDoc module to avoid namespace
-# clutter.
-#
-# ToDo: This isn't universally true.
-#
-# :include: README
+ ##
+ # Generator instance used for creating output
-module RDoc
+ attr_accessor :generator
- # Name of the dotfile that contains the description of files to be
- # processed in the current directory
- DOT_DOC_FILENAME = ".document"
+ ##
+ # Hash of files and their last modified times.
- # 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)
+ attr_reader :last_modified
+
+ ##
+ # RDoc options
+
+ attr_accessor :options
+
+ ##
+ # Accessor for statistics. Available after each call to parse_files
+
+ attr_reader :stats
+
+ ##
+ # The current documentation store
+
+ attr_reader :store
+
+ ##
+ # Add +klass+ that can generate output after parsing
+
+ def self.add_generator(klass)
+ name = klass.name.sub(/^RDoc::Generator::/, '').downcase
+ GENERATORS[name] = klass
+ end
+
+ ##
+ # Active RDoc::RDoc instance
+
+ def self.current
+ @current
+ end
+
+ ##
+ # Sets the active RDoc::RDoc instance
+
+ def self.current= rdoc
+ @current = rdoc
+ end
+
+ ##
+ # Creates a new RDoc::RDoc instance. Call #document to parse files and
+ # generate documentation.
+
+ def initialize
+ @current = nil
+ @exclude = nil
+ @generator = nil
+ @last_modified = {}
+ @old_siginfo = nil
+ @options = nil
+ @stats = nil
+ @store = nil
+ end
+
+ ##
+ # Report an error message and exit
+
+ def error(msg)
+ raise RDoc::Error, msg
+ end
+
+ ##
+ # Gathers a set of parseable files from the files and directories listed in
+ # +files+.
+
+ def gather_files files
+ files = ["."] if files.empty?
+
+ file_list = normalized_file_list files, true, @exclude
+
+ file_list = file_list.uniq
+
+ file_list = remove_unparseable file_list
+
+ file_list.sort
+ end
+
+ ##
+ # Turns RDoc from stdin into HTML
+
+ def handle_pipe
+ @html = RDoc::Markup::ToHtml.new @options
+
+ parser = RDoc::Text::MARKUP_FORMAT[@options.markup]
+
+ document = parser.parse $stdin.read
+
+ out = @html.convert document
+
+ $stdout.write out
+ end
+
+ ##
+ # Installs a siginfo handler that prints the current filename.
+
+ def install_siginfo_handler
+ return unless Signal.list.include? 'INFO'
+
+ @old_siginfo = trap 'INFO' do
+ puts @current if @current
end
end
+ ##
+ # Loads options from .rdoc_options if the file exists, otherwise creates a
+ # new RDoc::Options instance.
- # Exception thrown by any rdoc error. Only the #message part is
- # of use externally.
+ def load_options
+ options_file = File.expand_path '.rdoc_options'
+ return RDoc::Options.new unless File.exist? options_file
- class RDocError < Exception
+ RDoc.load_yaml
+
+ begin
+ options = YAML.load_file '.rdoc_options'
+ rescue Psych::SyntaxError
+ end
+
+ raise RDoc::Error, "#{options_file} is not a valid rdoc options file" unless
+ RDoc::Options === options
+
+ options
end
- # Encapsulate the production of rdoc documentation. Basically
- # you can use this as you would invoke rdoc from the command
- # line:
- #
- # rdoc = RDoc::RDoc.new
- # rdoc.document(args)
- #
- # where _args_ is an array of strings, each corresponding to
- # an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
- # for details.
-
- class RDoc
-
- ##
- # This is the list of output generators that we
- # support
-
- Generator = Struct.new(:file_name, :class_name, :key)
-
- GENERATORS = {}
- $:.collect {|d|
- File::expand_path(d)
- }.find_all {|d|
- File::directory?("#{d}/rdoc/generators")
- }.each {|dir|
- Dir::entries("#{dir}/rdoc/generators").each {|gen|
- next unless /(\w+)_generator.rb$/ =~ gen
- type = $1
- unless GENERATORS.has_key? type
- GENERATORS[type] = Generator.new("rdoc/generators/#{gen}",
- "#{type.upcase}Generator".intern,
- type)
+ ##
+ # Create an output dir if it doesn't exist. If it does exist, but doesn't
+ # contain the flag file <tt>created.rid</tt> then we refuse to use it, as
+ # we may clobber some manually generated documentation
+
+ def setup_output_dir(dir, force)
+ flag_file = output_flag_file dir
+
+ last = {}
+
+ if @options.dry_run then
+ # do nothing
+ elsif File.exist? dir then
+ error "#{dir} exists and is not a directory" unless File.directory? dir
+
+ begin
+ open flag_file do |io|
+ unless force then
+ Time.parse io.gets
+
+ io.each do |line|
+ file, time = line.split "\t", 2
+ time = Time.parse(time) rescue next
+ last[file] = time
+ end
+ end
end
- }
- }
-
- #######
- private
- #######
-
- ##
- # Report an error message and exit
-
- def error(msg)
- raise RDocError.new(msg)
+ rescue SystemCallError, TypeError
+ error <<-ERROR
+
+Directory #{dir} already exists, but it looks like it isn't an RDoc directory.
+
+Because RDoc doesn't want to risk destroying any of your existing files,
+you'll need to specify a different output directory name (using the --op <dir>
+option)
+
+ ERROR
+ end unless @options.force_output
+ else
+ FileUtils.mkdir_p dir
+ FileUtils.touch flag_file
end
-
- ##
- # Create an output dir if it doesn't exist. If it does
- # exist, but doesn't contain the flag file <tt>created.rid</tt>
- # then we refuse to use it, as we may clobber some
- # manually generated documentation
-
- def setup_output_dir(op_dir)
- flag_file = File.join(op_dir, "created.rid")
- if File.exist?(op_dir)
- unless File.directory?(op_dir)
- error "'#{op_dir}' exists, and is not a directory"
- end
- unless File.file?(flag_file)
- error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
- "isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
- "destroying any of your existing files, you'll need to\n" +
- "specify a different output directory name (using the\n" +
- "--op <dir> option).\n\n"
- end
- else
- File.makedirs(op_dir)
+
+ last
+ end
+
+ ##
+ # Sets the current documentation tree to +store+ and sets the store's rdoc
+ # driver to this instance.
+
+ def store= store
+ @store = store
+ @store.rdoc = self
+ end
+
+ ##
+ # Update the flag file in an output directory.
+
+ def update_output_dir(op_dir, time, last = {})
+ return if @options.dry_run or not @options.update_output_dir
+
+ open output_flag_file(op_dir), "w" do |f|
+ f.puts time.rfc2822
+ last.each do |n, t|
+ f.puts "#{n}\t#{t.rfc2822}"
end
- File.open(flag_file, "w") {|f| f.puts Time.now }
end
-
+ end
+
+ ##
+ # Return the path name of the flag file in an output directory.
- # The .document file contains a list of file and directory name
- # patterns, representing candidates for documentation. It may
- # also contain comments (starting with '#')
- def parse_dot_doc_file(in_dir, filename, options)
- # read and strip comments
- patterns = File.read(filename).gsub(/#.*/, '')
+ def output_flag_file(op_dir)
+ File.join op_dir, "created.rid"
+ end
- result = []
+ ##
+ # The .document file contains a list of file and directory name patterns,
+ # representing candidates for documentation. It may also contain comments
+ # (starting with '#')
- patterns.split.each do |patt|
- candidates = Dir.glob(File.join(in_dir, patt))
- result.concat(normalized_file_list(options, candidates))
- end
- result
+ def parse_dot_doc_file in_dir, filename
+ # read and strip comments
+ patterns = File.read(filename).gsub(/#.*/, '')
+
+ result = []
+
+ patterns.split.each do |patt|
+ candidates = Dir.glob(File.join(in_dir, patt))
+ result.concat normalized_file_list(candidates)
end
+ result
+ end
+
+ ##
+ # Given a list of files and directories, create a list of all the Ruby
+ # files they contain.
+ #
+ # If +force_doc+ is true we always add the given files, if false, only
+ # add files that we guarantee we can parse. It is true when looking at
+ # files given on the command line, false when recursing through
+ # subdirectories.
+ #
+ # The effect of this is that if you want a file with a non-standard
+ # extension parsed, you must name it explicitly.
+
+ def normalized_file_list(relative_files, force_doc = false,
+ exclude_pattern = nil)
+ file_list = []
+
+ relative_files.each do |rel_file_name|
+ next if rel_file_name.end_with? 'created.rid'
+ next if exclude_pattern && exclude_pattern =~ rel_file_name
+ stat = File.stat rel_file_name rescue next
+
+ case type = stat.ftype
+ when "file" then
+ next if last_modified = @last_modified[rel_file_name] and
+ stat.mtime.to_i <= last_modified.to_i
+
+ if force_doc or RDoc::Parser.can_parse(rel_file_name) then
+ file_list << rel_file_name.sub(/^\.\//, '')
+ @last_modified[rel_file_name] = stat.mtime
+ end
+ when "directory" then
+ next if rel_file_name == "CVS" || rel_file_name == ".svn"
- # Given a list of files and directories, create a list
- # of all the Ruby files they contain.
- #
- # If +force_doc+ is true, we always add the given files.
- # If false, only add files that we guarantee we can parse
- # It is true when looking at files given on the command line,
- # false when recursing through subdirectories.
- #
- # The effect of this is that if you want a file with a non-
- # standard extension parsed, you must name it explicity.
- #
-
- def normalized_file_list(options, relative_files, force_doc = false, exclude_pattern=nil)
- file_list = []
-
- relative_files.each do |rel_file_name|
- next if exclude_pattern && exclude_pattern =~ rel_file_name
- case type = File.stat(rel_file_name).ftype
- when "file"
- file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name)
- when "directory"
- next if rel_file_name == "CVS" || rel_file_name == ".svn"
- dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
- if File.file?(dot_doc)
- file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options))
- else
- file_list.concat(list_files_in_directory(rel_file_name, options))
- end
+ created_rid = File.join rel_file_name, "created.rid"
+ next if File.file? created_rid
+
+ dot_doc = File.join rel_file_name, RDoc::DOT_DOC_FILENAME
+
+ if File.file? dot_doc then
+ file_list << parse_dot_doc_file(rel_file_name, dot_doc)
else
- raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
+ file_list << list_files_in_directory(rel_file_name)
end
+ else
+ warn "rdoc can't parse the #{type} #{rel_file_name}"
end
- file_list
end
- # Return a list of the files to be processed in
- # a directory. We know that this directory doesn't have
- # a .document file, so we're looking for real files. However
- # we may well contain subdirectories which must
- # be tested for .document files
- def list_files_in_directory(dir, options)
- normalized_file_list(options, Dir.glob(File.join(dir, "*")), false, options.exclude)
+ file_list.flatten
+ end
+
+ ##
+ # Return a list of the files to be processed in a directory. We know that
+ # this directory doesn't have a .document file, so we're looking for real
+ # files. However we may well contain subdirectories which must be tested
+ # for .document files.
+
+ def list_files_in_directory dir
+ files = Dir.glob File.join(dir, "*")
+
+ normalized_file_list files, false, @options.exclude
+ end
+
+ ##
+ # Parses +filename+ and returns an RDoc::TopLevel
+
+ def parse_file filename
+ encoding = @options.encoding
+ filename = filename.encode encoding
+
+ @stats.add_file filename
+
+ return if RDoc::Parser.binary? filename
+
+ content = RDoc::Encoding.read_file filename, encoding
+
+ return unless content
+
+ filename_path = Pathname(filename).expand_path
+ begin
+ relative_path = filename_path.relative_path_from @options.root
+ rescue ArgumentError
+ relative_path = filename_path
+ end
+
+ if @options.page_dir and
+ relative_path.to_s.start_with? @options.page_dir.to_s then
+ relative_path =
+ relative_path.relative_path_from @options.page_dir
end
+ top_level = @store.add_file filename, relative_path.to_s
- # Parse each file on the command line, recursively entering
- # directories
+ parser = RDoc::Parser.for top_level, filename, content, @options, @stats
- def parse_files(options)
-
- file_info = []
+ return unless parser
- files = options.files
- files = ["."] if files.empty?
+ parser.scan
- file_list = normalized_file_list(options, files, true)
+ # restart documentation for the classes & modules found
+ top_level.classes_or_modules.each do |cm|
+ cm.done_documenting = false
+ end
- file_list.each do |fn|
- $stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
-
- content = File.open(fn, "r") {|f| f.read}
+ top_level
- top_level = TopLevel.new(fn)
- parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
- file_info << parser.scan
- @stats.num_files += 1
- end
+ rescue Errno::EACCES => e
+ $stderr.puts <<-EOF
+Unable to read #{filename}, #{e.message}
+
+Please check the permissions for this file. Perhaps you do not have access to
+it or perhaps the original author's permissions are to restrictive. If the
+this is not your library please report a bug to the author.
+ EOF
+ rescue => e
+ $stderr.puts <<-EOF
+Before reporting this, could you check that the file you're documenting
+has proper syntax:
+
+ #{Gem.ruby} -c #{filename}
+
+RDoc is not a full Ruby parser and will fail when fed invalid ruby programs.
+
+The internal error was:
+
+\t(#{e.class}) #{e.message}
+
+ EOF
+
+ $stderr.puts e.backtrace.join("\n\t") if $DEBUG_RDOC
+
+ raise e
+ nil
+ end
- file_info
+ ##
+ # Parse each file on the command line, recursively entering directories.
+
+ def parse_files files
+ file_list = gather_files files
+ @stats = RDoc::Stats.new @store, file_list.length, @options.verbosity
+
+ return [] if file_list.empty?
+
+ original_options = @options.dup
+ @stats.begin_adding
+
+ file_info = file_list.map do |filename|
+ @current = filename
+ parse_file filename
+ end.compact
+
+ @stats.done_adding
+ @options = original_options
+
+ file_info
+ end
+
+ ##
+ # Removes file extensions known to be unparseable from +files+ and TAGS
+ # files for emacs and vim.
+
+ def remove_unparseable files
+ files.reject do |file|
+ file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or
+ (file =~ /tags$/i and
+ open(file, 'rb') { |io|
+ io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/
+ })
end
+ end
+ ##
+ # Generates documentation or a coverage report depending upon the settings
+ # in +options+.
+ #
+ # +options+ can be either an RDoc::Options instance or an array of strings
+ # equivalent to the strings that would be passed on the command line like
+ # <tt>%w[-q -o doc -t My\ Doc\ Title]</tt>. #document will automatically
+ # call RDoc::Options#finish if an options instance was given.
+ #
+ # For a list of options, see either RDoc::Options or <tt>rdoc --help</tt>.
+ #
+ # By default, output will be stored in a directory called "doc" below the
+ # current directory, so make sure you're somewhere writable before invoking.
+
+ def document options
+ self.store = RDoc::Store.new
+
+ if RDoc::Options === options then
+ @options = options
+ @options.finish
+ else
+ @options = load_options
+ @options.parse options
+ end
- public
+ if @options.pipe then
+ handle_pipe
+ exit
+ end
- ###################################################################
- #
- # Format up one or more files according to the given arguments.
- # For simplicity, _argv_ is an array of strings, equivalent to the
- # strings that would be passed on the command line. (This isn't a
- # coincidence, as we _do_ pass in ARGV when running
- # interactively). For a list of options, see rdoc/rdoc.rb. By
- # default, output will be stored in a directory called +doc+ below
- # the current directory, so make sure you're somewhere writable
- # before invoking.
- #
- # Throws: RDocError on error
+ @exclude = @options.exclude
- def document(argv)
+ unless @options.coverage_report then
+ @last_modified = setup_output_dir @options.op_dir, @options.force_update
+ end
- TopLevel::reset
+ @store.encoding = @options.encoding
+ @store.dry_run = @options.dry_run
+ @store.main = @options.main_page
+ @store.title = @options.title
+ @store.path = @options.op_dir
- @stats = Stats.new
+ @start_time = Time.now
- options = Options.instance
- options.parse(argv, GENERATORS)
-
- unless options.all_one_file
- setup_output_dir(options.op_dir)
- end
+ @store.load_cache
+
+ file_info = parse_files @options.files
+
+ @options.default_title = "RDoc Documentation"
+
+ @store.complete @options.visibility
- file_info = parse_files(options)
+ @stats.coverage_level = @options.coverage_report
- gen = options.generator
-
- $stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet
-
- require gen.file_name
-
- gen_class = Generators.const_get(gen.class_name)
-
- unless file_info.empty?
- gen = gen_class.for(options)
+ if @options.coverage_report then
+ puts
- pwd = Dir.pwd
+ puts @stats.report.accept RDoc::Markup::ToRdoc.new
+ elsif file_info.empty? then
+ $stderr.puts "\nNo newer files." unless @options.quiet
+ else
+ gen_klass = @options.generator
- Dir.chdir(options.op_dir) unless options.all_one_file
+ @generator = gen_klass.new @store, @options
- begin
- Diagram.new(file_info, options).draw if options.diagram
- gen.generate(file_info)
- ensure
- Dir.chdir(pwd)
+ generate
+ end
+
+ if @stats and (@options.coverage_report or not @options.quiet) then
+ puts
+ puts @stats.summary.accept RDoc::Markup::ToRdoc.new
+ end
+
+ exit @stats.fully_documented? if @options.coverage_report
+ end
+
+ ##
+ # Generates documentation for +file_info+ (from #parse_files) into the
+ # output dir using the generator selected
+ # by the RDoc options
+
+ def generate
+ if @options.dry_run then
+ # do nothing
+ @generator.generate
+ else
+ Dir.chdir @options.op_dir do
+ unless @options.quiet then
+ $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
end
- end
- unless options.quiet
- puts
- @stats.print
+ @generator.generate
+ update_output_dir '.', @start_time, @last_modified
end
end
end
+
+ ##
+ # Removes a siginfo handler and replaces the previous
+
+ def remove_siginfo_handler
+ return unless Signal.list.key? 'INFO'
+
+ handler = @old_siginfo || 'DEFAULT'
+
+ trap 'INFO', handler
+ end
+
+end
+
+begin
+ require 'rubygems'
+
+ rdoc_extensions = Gem.find_files 'rdoc/discover'
+
+ rdoc_extensions.each do |extension|
+ begin
+ load extension
+ rescue => e
+ warn "error loading #{extension.inspect}: #{e.message} (#{e.class})"
+ warn "\t#{e.backtrace.join "\n\t"}" if $DEBUG
+ end
+ end
+rescue LoadError
end
+# require built-in generators after discovery in case they've been replaced
+require 'rdoc/generator/darkfish'
+require 'rdoc/generator/ri'
+require 'rdoc/generator/pot'
diff --git a/lib/rdoc/require.rb b/lib/rdoc/require.rb
new file mode 100644
index 0000000000..91f9c24e5d
--- /dev/null
+++ b/lib/rdoc/require.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+##
+# A file loaded by \#require
+
+class RDoc::Require < RDoc::CodeObject
+
+ ##
+ # Name of the required file
+
+ attr_accessor :name
+
+ ##
+ # Creates a new Require that loads +name+ with +comment+
+
+ def initialize(name, comment)
+ super()
+ @name = name.gsub(/'|"/, "") #'
+ @top_level = nil
+ self.comment = comment
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x require '%s' in %s>" % [
+ self.class,
+ object_id,
+ @name,
+ parent_file_name,
+ ]
+ end
+
+ def to_s # :nodoc:
+ "require #{name} in: #{parent}"
+ end
+
+ ##
+ # The RDoc::TopLevel corresponding to this require, or +nil+ if not found.
+
+ def top_level
+ @top_level ||= begin
+ tl = RDoc::TopLevel.all_files_hash[name + '.rb']
+
+ if tl.nil? and RDoc::TopLevel.all_files.first.full_name =~ %r(^lib/) then
+ # second chance
+ tl = RDoc::TopLevel.all_files_hash['lib/' + name + '.rb']
+ end
+
+ tl
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/ri.rb b/lib/rdoc/ri.rb
new file mode 100644
index 0000000000..c798c1fc49
--- /dev/null
+++ b/lib/rdoc/ri.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'rdoc'
+
+##
+# Namespace for the ri command line tool's implementation.
+#
+# See <tt>ri --help</tt> for details.
+
+module RDoc::RI
+
+ ##
+ # Base RI error class
+
+ class Error < RDoc::Error; end
+
+ autoload :Driver, 'rdoc/ri/driver'
+ autoload :Paths, 'rdoc/ri/paths'
+ autoload :Store, 'rdoc/ri/store'
+
+end
+
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
new file mode 100644
index 0000000000..fa0e040a42
--- /dev/null
+++ b/lib/rdoc/ri/driver.rb
@@ -0,0 +1,1547 @@
+# frozen_string_literal: true
+require 'abbrev'
+require 'optparse'
+
+begin
+ require 'readline'
+rescue LoadError
+end
+
+begin
+ require 'win32console'
+rescue LoadError
+end
+
+require 'rdoc'
+
+##
+# For RubyGems backwards compatibility
+
+require 'rdoc/ri/formatter'
+
+##
+# The RI driver implements the command-line ri tool.
+#
+# The driver supports:
+# * loading RI data from:
+# * Ruby's standard library
+# * RubyGems
+# * ~/.rdoc
+# * A user-supplied directory
+# * Paging output (uses RI_PAGER environment variable, PAGER environment
+# variable or the less, more and pager programs)
+# * Interactive mode with tab-completion
+# * Abbreviated names (ri Zl shows Zlib documentation)
+# * Colorized output
+# * Merging output from multiple RI data sources
+
+class RDoc::RI::Driver
+
+ ##
+ # Base Driver error class
+
+ class Error < RDoc::RI::Error; end
+
+ ##
+ # Raised when a name isn't found in the ri data stores
+
+ class NotFoundError < Error
+
+ def initialize(klass, suggestions = nil) # :nodoc:
+ @klass = klass
+ @suggestions = suggestions
+ end
+
+ ##
+ # Name that wasn't found
+
+ def name
+ @klass
+ end
+
+ def message # :nodoc:
+ str = "Nothing known about #{@klass}"
+ if @suggestions and !@suggestions.empty?
+ str += "\nDid you mean? #{@suggestions.join("\n ")}"
+ end
+ str
+ end
+ end
+
+ ##
+ # Show all method documentation following a class or module
+
+ attr_accessor :show_all
+
+ ##
+ # An RDoc::RI::Store for each entry in the RI path
+
+ attr_accessor :stores
+
+ ##
+ # Controls the user of the pager vs $stdout
+
+ attr_accessor :use_stdout
+
+ ##
+ # Default options for ri
+
+ def self.default_options
+ options = {}
+ options[:interactive] = false
+ options[:profile] = false
+ options[:show_all] = false
+ options[:use_stdout] = !$stdout.tty?
+ options[:width] = 72
+
+ # By default all standard paths are used.
+ options[:use_system] = true
+ options[:use_site] = true
+ options[:use_home] = true
+ options[:use_gems] = true
+ options[:extra_doc_dirs] = []
+
+ return options
+ end
+
+ ##
+ # Dump +data_path+ using pp
+
+ def self.dump data_path
+ require 'pp'
+
+ open data_path, 'rb' do |io|
+ pp Marshal.load(io.read)
+ end
+ end
+
+ ##
+ # Parses +argv+ and returns a Hash of options
+
+ def self.process_args argv
+ options = default_options
+
+ opts = OptionParser.new do |opt|
+ opt.accept File do |file,|
+ File.readable?(file) and not File.directory?(file) and file
+ end
+
+ opt.program_name = File.basename $0
+ opt.version = RDoc::VERSION
+ opt.release = nil
+ opt.summary_indent = ' ' * 4
+
+ opt.banner = <<-EOT
+Usage: #{opt.program_name} [options] [name ...]
+
+Where name can be:
+
+ Class | Module | Module::Class
+
+ Class::method | Class#method | Class.method | method
+
+ gem_name: | gem_name:README | gem_name:History
+
+All class names may be abbreviated to their minimum unambiguous form.
+If a name is ambiguous, all valid options will be listed.
+
+A '.' matches either class or instance methods, while #method
+matches only instance and ::method matches only class methods.
+
+README and other files may be displayed by prefixing them with the gem name
+they're contained in. If the gem name is followed by a ':' all files in the
+gem will be shown. The file name extension may be omitted where it is
+unambiguous.
+
+For example:
+
+ #{opt.program_name} Fil
+ #{opt.program_name} File
+ #{opt.program_name} File.new
+ #{opt.program_name} zip
+ #{opt.program_name} rdoc:README
+
+Note that shell quoting or escaping may be required for method names
+containing punctuation:
+
+ #{opt.program_name} 'Array.[]'
+ #{opt.program_name} compact\\!
+
+To see the default directories #{opt.program_name} will search, run:
+
+ #{opt.program_name} --list-doc-dirs
+
+Specifying the --system, --site, --home, --gems, or --doc-dir options
+will limit ri to searching only the specified directories.
+
+ri options may be set in the RI environment variable.
+
+The ri pager can be set with the RI_PAGER environment variable
+or the PAGER environment variable.
+ EOT
+
+ opt.separator nil
+ opt.separator "Options:"
+
+ opt.separator nil
+
+ opt.on("--[no-]interactive", "-i",
+ "In interactive mode you can repeatedly",
+ "look up methods with autocomplete.") do |interactive|
+ options[:interactive] = interactive
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]all", "-a",
+ "Show all documentation for a class or",
+ "module.") do |show_all|
+ options[:show_all] = show_all
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]list", "-l",
+ "List classes ri knows about.") do |list|
+ options[:list] = list
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]pager",
+ "Send output to a pager,",
+ "rather than directly to stdout.") do |use_pager|
+ options[:use_stdout] = !use_pager
+ end
+
+ opt.separator nil
+
+ opt.on("-T",
+ "Synonym for --no-pager.") do
+ options[:use_stdout] = true
+ end
+
+ opt.separator nil
+
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
+ "Set the width of the output.") do |width|
+ options[:width] = width
+ end
+
+ opt.separator nil
+
+ opt.on("--server[=PORT]", Integer,
+ "Run RDoc server on the given port.",
+ "The default port is 8214.") do |port|
+ options[:server] = port || 8214
+ end
+
+ opt.separator nil
+
+ formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
+ formatters = formatters.sort.map do |formatter|
+ formatter.to_s.sub('To', '').downcase
+ end
+ formatters -= %w[html label test] # remove useless output formats
+
+ opt.on("--format=NAME", "-f",
+ "Use the selected formatter. The default",
+ "formatter is bs for paged output and ansi",
+ "otherwise. Valid formatters are:",
+ "#{formatters.join(', ')}.", formatters) do |value|
+ options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
+ end
+
+ opt.separator nil
+
+ opt.on("--help", "-h",
+ "Show help and exit.") do
+ puts opts
+ exit
+ end
+
+ opt.separator nil
+
+ opt.on("--version", "-v",
+ "Output version information and exit.") do
+ puts "#{opts.program_name} #{opts.version}"
+ exit
+ end
+
+ opt.separator nil
+ opt.separator "Data source options:"
+ opt.separator nil
+
+ opt.on("--[no-]list-doc-dirs",
+ "List the directories from which ri will",
+ "source documentation on stdout and exit.") do |list_doc_dirs|
+ options[:list_doc_dirs] = list_doc_dirs
+ end
+
+ opt.separator nil
+
+ opt.on("--doc-dir=DIRNAME", "-d", Array,
+ "List of directories from which to source",
+ "documentation in addition to the standard",
+ "directories. May be repeated.") do |value|
+ value.each do |dir|
+ unless File.directory? dir then
+ raise OptionParser::InvalidArgument, "#{dir} is not a directory"
+ end
+
+ options[:extra_doc_dirs] << File.expand_path(dir)
+ end
+ end
+
+ opt.separator nil
+
+ opt.on("--no-standard-docs",
+ "Do not include documentation from",
+ "the Ruby standard library, site_lib,",
+ "installed gems, or ~/.rdoc.",
+ "Use with --doc-dir.") do
+ options[:use_system] = false
+ options[:use_site] = false
+ options[:use_gems] = false
+ options[:use_home] = false
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]system",
+ "Include documentation from Ruby's",
+ "standard library. Defaults to true.") do |value|
+ options[:use_system] = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]site",
+ "Include documentation from libraries",
+ "installed in site_lib.",
+ "Defaults to true.") do |value|
+ options[:use_site] = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]gems",
+ "Include documentation from RubyGems.",
+ "Defaults to true.") do |value|
+ options[:use_gems] = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]home",
+ "Include documentation stored in ~/.rdoc.",
+ "Defaults to true.") do |value|
+ options[:use_home] = value
+ end
+
+ opt.separator nil
+ opt.separator "Debug options:"
+ opt.separator nil
+
+ opt.on("--[no-]profile",
+ "Run with the ruby profiler.") do |value|
+ options[:profile] = value
+ end
+
+ opt.separator nil
+
+ opt.on("--dump=CACHE", File,
+ "Dump data from an ri cache or data file.") do |value|
+ options[:dump_path] = value
+ end
+ end
+
+ argv = ENV['RI'].to_s.split.concat argv
+
+ opts.parse! argv
+
+ options[:names] = argv
+
+ options[:use_stdout] ||= !$stdout.tty?
+ options[:use_stdout] ||= options[:interactive]
+ options[:width] ||= 72
+
+ options
+
+ rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
+ puts opts
+ puts
+ puts e
+ exit 1
+ end
+
+ ##
+ # Runs the ri command line executable using +argv+
+
+ def self.run argv = ARGV
+ options = process_args argv
+
+ if options[:dump_path] then
+ dump options[:dump_path]
+ return
+ end
+
+ ri = new options
+ ri.run
+ end
+
+ ##
+ # Creates a new driver using +initial_options+ from ::process_args
+
+ def initialize initial_options = {}
+ @paging = false
+ @classes = nil
+
+ options = self.class.default_options.update(initial_options)
+
+ @formatter_klass = options[:formatter]
+
+ require 'profile' if options[:profile]
+
+ @names = options[:names]
+ @list = options[:list]
+
+ @doc_dirs = []
+ @stores = []
+
+ RDoc::RI::Paths.each(options[:use_system], options[:use_site],
+ options[:use_home], options[:use_gems],
+ *options[:extra_doc_dirs]) do |path, type|
+ @doc_dirs << path
+
+ store = RDoc::RI::Store.new path, type
+ store.load_cache
+ @stores << store
+ end
+
+ @list_doc_dirs = options[:list_doc_dirs]
+
+ @interactive = options[:interactive]
+ @server = options[:server]
+ @use_stdout = options[:use_stdout]
+ @show_all = options[:show_all]
+
+ # pager process for jruby
+ @jruby_pager_process = nil
+ end
+
+ ##
+ # Adds paths for undocumented classes +also_in+ to +out+
+
+ def add_also_in out, also_in
+ return if also_in.empty?
+
+ out << RDoc::Markup::Rule.new(1)
+ out << RDoc::Markup::Paragraph.new("Also found in:")
+
+ paths = RDoc::Markup::Verbatim.new
+ also_in.each do |store|
+ paths.parts.push store.friendly_path, "\n"
+ end
+ out << paths
+ end
+
+ ##
+ # Adds a class header to +out+ for class +name+ which is described in
+ # +classes+.
+
+ def add_class out, name, classes
+ heading = if classes.all? { |klass| klass.module? } then
+ name
+ else
+ superclass = classes.map do |klass|
+ klass.superclass unless klass.module?
+ end.compact.shift || 'Object'
+
+ superclass = superclass.full_name unless String === superclass
+
+ "#{name} < #{superclass}"
+ end
+
+ out << RDoc::Markup::Heading.new(1, heading)
+ out << RDoc::Markup::BlankLine.new
+ end
+
+ ##
+ # Adds "(from ...)" to +out+ for +store+
+
+ def add_from out, store
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
+ end
+
+ ##
+ # Adds +extends+ to +out+
+
+ def add_extends out, extends
+ add_extension_modules out, 'Extended by', extends
+ end
+
+ ##
+ # Adds a list of +extensions+ to this module of the given +type+ to +out+.
+ # add_includes and add_extends call this, so you should use those directly.
+
+ def add_extension_modules out, type, extensions
+ return if extensions.empty?
+
+ out << RDoc::Markup::Rule.new(1)
+ out << RDoc::Markup::Heading.new(1, "#{type}:")
+
+ extensions.each do |modules, store|
+ if modules.length == 1 then
+ add_extension_modules_single out, store, modules.first
+ else
+ add_extension_modules_multiple out, store, modules
+ end
+ end
+ end
+
+ ##
+ # Renders multiple included +modules+ from +store+ to +out+.
+
+ def add_extension_modules_multiple out, store, modules # :nodoc:
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
+
+ wout, with = modules.partition { |incl| incl.comment.empty? }
+
+ out << RDoc::Markup::BlankLine.new unless with.empty?
+
+ with.each do |incl|
+ out << RDoc::Markup::Paragraph.new(incl.name)
+ out << RDoc::Markup::BlankLine.new
+ out << incl.comment
+ end
+
+ unless wout.empty? then
+ verb = RDoc::Markup::Verbatim.new
+
+ wout.each do |incl|
+ verb.push incl.name, "\n"
+ end
+
+ out << verb
+ end
+ end
+
+ ##
+ # Adds a single extension module +include+ from +store+ to +out+
+
+ def add_extension_modules_single out, store, include # :nodoc:
+ name = include.name
+ path = store.friendly_path
+ out << RDoc::Markup::Paragraph.new("#{name} (from #{path})")
+
+ if include.comment then
+ out << RDoc::Markup::BlankLine.new
+ out << include.comment
+ end
+ end
+
+ ##
+ # Adds +includes+ to +out+
+
+ def add_includes out, includes
+ add_extension_modules out, 'Includes', includes
+ end
+
+ ##
+ # Looks up the method +name+ and adds it to +out+
+
+ def add_method out, name
+ filtered = lookup_method name
+
+ method_out = method_document name, filtered
+
+ out.concat method_out.parts
+ end
+
+ ##
+ # Adds documentation for all methods in +klass+ to +out+
+
+ def add_method_documentation out, klass
+ klass.method_list.each do |method|
+ begin
+ add_method out, method.full_name
+ rescue NotFoundError
+ next
+ end
+ end
+ end
+
+ ##
+ # Adds a list of +methods+ to +out+ with a heading of +name+
+
+ def add_method_list out, methods, name
+ return if methods.empty?
+
+ out << RDoc::Markup::Heading.new(1, "#{name}:")
+ out << RDoc::Markup::BlankLine.new
+
+ if @use_stdout and !@interactive then
+ out.concat methods.map { |method|
+ RDoc::Markup::Verbatim.new method
+ }
+ else
+ out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', '))
+ end
+
+ out << RDoc::Markup::BlankLine.new
+ end
+
+ ##
+ # Returns ancestor classes of +klass+
+
+ def ancestors_of klass
+ ancestors = []
+
+ unexamined = [klass]
+ seen = []
+
+ loop do
+ break if unexamined.empty?
+ current = unexamined.shift
+ seen << current
+
+ stores = classes[current]
+
+ break unless stores and not stores.empty?
+
+ klasses = stores.map do |store|
+ store.ancestors[current]
+ end.flatten.uniq
+
+ klasses = klasses - seen
+
+ ancestors.concat klasses
+ unexamined.concat klasses
+ end
+
+ ancestors.reverse
+ end
+
+ ##
+ # For RubyGems backwards compatibility
+
+ def class_cache # :nodoc:
+ end
+
+ ##
+ # Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
+
+ def class_document name, found, klasses, includes, extends
+ also_in = []
+
+ out = RDoc::Markup::Document.new
+
+ add_class out, name, klasses
+
+ add_includes out, includes
+ add_extends out, extends
+
+ found.each do |store, klass|
+ render_class out, store, klass, also_in
+ end
+
+ add_also_in out, also_in
+
+ out
+ end
+
+ ##
+ # Adds the class +comment+ to +out+.
+
+ def class_document_comment out, comment # :nodoc:
+ unless comment.empty? then
+ out << RDoc::Markup::Rule.new(1)
+
+ if comment.merged? then
+ parts = comment.parts
+ parts = parts.zip [RDoc::Markup::BlankLine.new] * parts.length
+ parts.flatten!
+ parts.pop
+
+ out.concat parts
+ else
+ out << comment
+ end
+ end
+ end
+
+ ##
+ # Adds the constants from +klass+ to the Document +out+.
+
+ def class_document_constants out, klass # :nodoc:
+ return if klass.constants.empty?
+
+ out << RDoc::Markup::Heading.new(1, "Constants:")
+ out << RDoc::Markup::BlankLine.new
+ list = RDoc::Markup::List.new :NOTE
+
+ constants = klass.constants.sort_by { |constant| constant.name }
+
+ list.items.concat constants.map { |constant|
+ parts = constant.comment.parts if constant.comment
+ parts << RDoc::Markup::Paragraph.new('[not documented]') if
+ parts.empty?
+
+ RDoc::Markup::ListItem.new(constant.name, *parts)
+ }
+
+ out << list
+ out << RDoc::Markup::BlankLine.new
+ end
+
+ ##
+ # Hash mapping a known class or module to the stores it can be loaded from
+
+ def classes
+ return @classes if @classes
+
+ @classes = {}
+
+ @stores.each do |store|
+ store.cache[:modules].each do |mod|
+ # using default block causes searched-for modules to be added
+ @classes[mod] ||= []
+ @classes[mod] << store
+ end
+ end
+
+ @classes
+ end
+
+ ##
+ # Returns the stores wherein +name+ is found along with the classes,
+ # extends and includes that match it
+
+ def classes_and_includes_and_extends_for name
+ klasses = []
+ extends = []
+ includes = []
+
+ found = @stores.map do |store|
+ begin
+ klass = store.load_class name
+ klasses << klass
+ extends << [klass.extends, store] if klass.extends
+ includes << [klass.includes, store] if klass.includes
+ [store, klass]
+ rescue RDoc::Store::MissingFileError
+ end
+ end.compact
+
+ extends.reject! do |modules,| modules.empty? end
+ includes.reject! do |modules,| modules.empty? end
+
+ [found, klasses, includes, extends]
+ end
+
+ ##
+ # Completes +name+ based on the caches. For Readline
+
+ def complete name
+ completions = []
+
+ klass, selector, method = parse_name name
+
+ complete_klass name, klass, selector, method, completions
+ complete_method name, klass, selector, completions
+
+ completions.sort.uniq
+ end
+
+ def complete_klass name, klass, selector, method, completions # :nodoc:
+ klasses = classes.keys
+
+ # may need to include Foo when given Foo::
+ klass_name = method ? name : klass
+
+ if name !~ /#|\./ then
+ completions.replace klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/)
+ completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if
+ name =~ /::$/
+
+ completions << klass if classes.key? klass # to complete a method name
+ elsif selector then
+ completions << klass if classes.key? klass
+ elsif classes.key? klass_name then
+ completions << klass_name
+ end
+ end
+
+ def complete_method name, klass, selector, completions # :nodoc:
+ if completions.include? klass and name =~ /#|\.|::/ then
+ methods = list_methods_matching name
+
+ if not methods.empty? then
+ # remove Foo if given Foo:: and a method was found
+ completions.delete klass
+ elsif selector then
+ # replace Foo with Foo:: as given
+ completions.delete klass
+ completions << "#{klass}#{selector}"
+ end
+
+ completions.concat methods
+ end
+ end
+
+ ##
+ # Converts +document+ to text and writes it to the pager
+
+ def display document
+ page do |io|
+ text = document.accept formatter(io)
+
+ io.write text
+ end
+ end
+
+ ##
+ # Outputs formatted RI data for class +name+. Groups undocumented classes
+
+ def display_class name
+ return if name =~ /#|\./
+
+ found, klasses, includes, extends =
+ classes_and_includes_and_extends_for name
+
+ return if found.empty?
+
+ out = class_document name, found, klasses, includes, extends
+
+ display out
+ end
+
+ ##
+ # Outputs formatted RI data for method +name+
+
+ def display_method name
+ out = RDoc::Markup::Document.new
+
+ add_method out, name
+
+ display out
+ end
+
+ ##
+ # Outputs formatted RI data for the class or method +name+.
+ #
+ # Returns true if +name+ was found, false if it was not an alternative could
+ # be guessed, raises an error if +name+ couldn't be guessed.
+
+ def display_name name
+ if name =~ /\w:(\w|$)/ then
+ display_page name
+ return true
+ end
+
+ return true if display_class name
+
+ display_method name if name =~ /::|#|\./
+
+ true
+ rescue NotFoundError
+ matches = list_methods_matching name if name =~ /::|#|\./
+ matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty?
+
+ raise if matches.empty?
+
+ page do |io|
+ io.puts "#{name} not found, maybe you meant:"
+ io.puts
+ io.puts matches.sort.join("\n")
+ end
+
+ false
+ end
+
+ ##
+ # Displays each name in +name+
+
+ def display_names names
+ names.each do |name|
+ name = expand_name name
+
+ display_name name
+ end
+ end
+
+ ##
+ # Outputs formatted RI data for page +name+.
+
+ def display_page name
+ store_name, page_name = name.split ':', 2
+
+ store = @stores.find { |s| s.source == store_name }
+
+ return display_page_list store if page_name.empty?
+
+ pages = store.cache[:pages]
+
+ unless pages.include? page_name then
+ found_names = pages.select do |n|
+ n =~ /#{Regexp.escape page_name}\.[^.]+$/
+ end
+
+ if found_names.length.zero? then
+ return display_page_list store, pages
+ elsif found_names.length > 1 then
+ return display_page_list store, found_names, page_name
+ end
+
+ page_name = found_names.first
+ end
+
+ page = store.load_page page_name
+
+ display page.comment
+ end
+
+ ##
+ # Outputs a formatted RI page list for the pages in +store+.
+
+ def display_page_list store, pages = store.cache[:pages], search = nil
+ out = RDoc::Markup::Document.new
+
+ title = if search then
+ "#{search} pages"
+ else
+ 'Pages'
+ end
+
+ out << RDoc::Markup::Heading.new(1, "#{title} in #{store.friendly_path}")
+ out << RDoc::Markup::BlankLine.new
+
+ list = RDoc::Markup::List.new(:BULLET)
+
+ pages.each do |page|
+ list << RDoc::Markup::Paragraph.new(page)
+ end
+
+ out << list
+
+ display out
+ end
+
+ def check_did_you_mean # :nodoc:
+ if defined? DidYouMean::SpellChecker
+ true
+ else
+ begin
+ require 'did_you_mean'
+ if defined? DidYouMean::SpellChecker
+ true
+ else
+ false
+ end
+ rescue LoadError
+ false
+ end
+ end
+ end
+
+ ##
+ # Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
+ # will be expanded to Zlib::DataError.
+
+ def expand_class klass
+ class_names = classes.keys
+ ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
+ if ary.length != 1 && ary.first != klass
+ if check_did_you_mean
+ suggestions = DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass)
+ raise NotFoundError.new(klass, suggestions)
+ else
+ raise NotFoundError, klass
+ end
+ end
+ ary.first
+ end
+
+ ##
+ # Expands the class portion of +name+ into a fully-qualified class. See
+ # #expand_class.
+
+ def expand_name name
+ klass, selector, method = parse_name name
+
+ return [selector, method].join if klass.empty?
+
+ case selector
+ when ':' then
+ [find_store(klass), selector, method]
+ else
+ [expand_class(klass), selector, method]
+ end.join
+ end
+
+ ##
+ # Filters the methods in +found+ trying to find a match for +name+.
+
+ def filter_methods found, name
+ regexp = name_regexp name
+
+ filtered = found.find_all do |store, methods|
+ methods.any? { |method| method.full_name =~ regexp }
+ end
+
+ return filtered unless filtered.empty?
+
+ found
+ end
+
+ ##
+ # Yields items matching +name+ including the store they were found in, the
+ # class being searched for, the class they were found in (an ancestor) the
+ # types of methods to look up (from #method_type), and the method name being
+ # searched for
+
+ def find_methods name
+ klass, selector, method = parse_name name
+
+ types = method_type selector
+
+ klasses = nil
+ ambiguous = klass.empty?
+
+ if ambiguous then
+ klasses = classes.keys
+ else
+ klasses = ancestors_of klass
+ klasses.unshift klass
+ end
+
+ methods = []
+
+ klasses.each do |ancestor|
+ ancestors = classes[ancestor]
+
+ next unless ancestors
+
+ klass = ancestor if ambiguous
+
+ ancestors.each do |store|
+ methods << [store, klass, ancestor, types, method]
+ end
+ end
+
+ methods = methods.sort_by do |_, k, a, _, m|
+ [k, a, m].compact
+ end
+
+ methods.each do |item|
+ yield(*item) # :yields: store, klass, ancestor, types, method
+ end
+
+ self
+ end
+
+ ##
+ # Finds the given +pager+ for jruby. Returns an IO if +pager+ was found.
+ #
+ # Returns false if +pager+ does not exist.
+ #
+ # Returns nil if the jruby JVM doesn't support ProcessBuilder redirection
+ # (1.6 and older).
+
+ def find_pager_jruby pager
+ require 'java'
+ require 'shellwords'
+
+ return nil unless java.lang.ProcessBuilder.constants.include? :Redirect
+
+ pager = Shellwords.split pager
+
+ pb = java.lang.ProcessBuilder.new(*pager)
+ pb = pb.redirect_output java.lang.ProcessBuilder::Redirect::INHERIT
+
+ @jruby_pager_process = pb.start
+
+ input = @jruby_pager_process.output_stream
+
+ io = input.to_io
+ io.sync = true
+ io
+ rescue java.io.IOException
+ false
+ end
+
+ ##
+ # Finds a store that matches +name+ which can be the name of a gem, "ruby",
+ # "home" or "site".
+ #
+ # See also RDoc::Store#source
+
+ def find_store name
+ @stores.each do |store|
+ source = store.source
+
+ return source if source == name
+
+ return source if
+ store.type == :gem and source =~ /^#{Regexp.escape name}-\d/
+ end
+
+ raise RDoc::RI::Driver::NotFoundError, name
+ end
+
+ ##
+ # Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
+ # use it. If we're outputting to a pager, use bs, otherwise ansi.
+
+ def formatter(io)
+ if @formatter_klass then
+ @formatter_klass.new
+ elsif paging? or !io.tty? then
+ RDoc::Markup::ToBs.new
+ else
+ RDoc::Markup::ToAnsi.new
+ end
+ end
+
+ ##
+ # Runs ri interactively using Readline if it is available.
+
+ def interactive
+ puts "\nEnter the method name you want to look up."
+
+ if defined? Readline then
+ Readline.completion_proc = method :complete
+ puts "You can use tab to autocomplete."
+ end
+
+ puts "Enter a blank line to exit.\n\n"
+
+ loop do
+ name = if defined? Readline then
+ Readline.readline ">> "
+ else
+ print ">> "
+ $stdin.gets
+ end
+
+ return if name.nil? or name.empty?
+
+ begin
+ display_name expand_name(name.strip)
+ rescue NotFoundError => e
+ puts e.message
+ end
+ end
+
+ rescue Interrupt
+ exit
+ end
+
+ ##
+ # Is +file+ in ENV['PATH']?
+
+ def in_path? file
+ return true if file =~ %r%\A/% and File.exist? file
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
+ File.exist? File.join(path, file)
+ end
+ end
+
+ ##
+ # Lists classes known to ri starting with +names+. If +names+ is empty all
+ # known classes are shown.
+
+ def list_known_classes names = []
+ classes = []
+
+ stores.each do |store|
+ classes << store.module_names
+ end
+
+ classes = classes.flatten.uniq.sort
+
+ unless names.empty? then
+ filter = Regexp.union names.map { |name| /^#{name}/ }
+
+ classes = classes.grep filter
+ end
+
+ page do |io|
+ if paging? or io.tty? then
+ if names.empty? then
+ io.puts "Classes and Modules known to ri:"
+ else
+ io.puts "Classes and Modules starting with #{names.join ', '}:"
+ end
+ io.puts
+ end
+
+ io.puts classes.join("\n")
+ end
+ end
+
+ ##
+ # Returns an Array of methods matching +name+
+
+ def list_methods_matching name
+ found = []
+
+ find_methods name do |store, klass, ancestor, types, method|
+ if types == :instance or types == :both then
+ methods = store.instance_methods[ancestor]
+
+ if methods then
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
+
+ matches = matches.map do |match|
+ "#{klass}##{match}"
+ end
+
+ found.concat matches
+ end
+ end
+
+ if types == :class or types == :both then
+ methods = store.class_methods[ancestor]
+
+ next unless methods
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
+
+ matches = matches.map do |match|
+ "#{klass}::#{match}"
+ end
+
+ found.concat matches
+ end
+ end
+
+ found.uniq
+ end
+
+ ##
+ # Loads RI data for method +name+ on +klass+ from +store+. +type+ and
+ # +cache+ indicate if it is a class or instance method.
+
+ def load_method store, cache, klass, type, name
+ methods = store.send(cache)[klass]
+
+ return unless methods
+
+ method = methods.find do |method_name|
+ method_name == name
+ end
+
+ return unless method
+
+ store.load_method klass, "#{type}#{method}"
+ rescue RDoc::Store::MissingFileError => e
+ comment = RDoc::Comment.new("missing documentation at #{e.file}").parse
+
+ method = RDoc::AnyMethod.new nil, name
+ method.comment = comment
+ method
+ end
+
+ ##
+ # Returns an Array of RI data for methods matching +name+
+
+ def load_methods_matching name
+ found = []
+
+ find_methods name do |store, klass, ancestor, types, method|
+ methods = []
+
+ methods << load_method(store, :class_methods, ancestor, '::', method) if
+ [:class, :both].include? types
+
+ methods << load_method(store, :instance_methods, ancestor, '#', method) if
+ [:instance, :both].include? types
+
+ found << [store, methods.compact]
+ end
+
+ found.reject do |path, methods| methods.empty? end
+ end
+
+ ##
+ # Returns a filtered list of methods matching +name+
+
+ def lookup_method name
+ found = load_methods_matching name
+
+ if found.empty?
+ if check_did_you_mean
+ methods = []
+ _, _, method_name = parse_name name
+ find_methods name do |store, klass, ancestor, types, method|
+ methods.push(*store.class_methods[klass]) if [:class, :both].include? types
+ methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
+ end
+ methods = methods.uniq
+ suggestions = DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name)
+ raise NotFoundError.new(name, suggestions)
+ else
+ raise NotFoundError, name
+ end
+ end
+
+ filter_methods found, name
+ end
+
+ ##
+ # Builds a RDoc::Markup::Document from +found+, +klasses+ and +includes+
+
+ def method_document name, filtered
+ out = RDoc::Markup::Document.new
+
+ out << RDoc::Markup::Heading.new(1, name)
+ out << RDoc::Markup::BlankLine.new
+
+ filtered.each do |store, methods|
+ methods.each do |method|
+ render_method out, store, method, name
+ end
+ end
+
+ out
+ end
+
+ ##
+ # Returns the type of method (:both, :instance, :class) for +selector+
+
+ def method_type selector
+ case selector
+ when '.', nil then :both
+ when '#' then :instance
+ else :class
+ end
+ end
+
+ ##
+ # Returns a regular expression for +name+ that will match an
+ # RDoc::AnyMethod's name.
+
+ def name_regexp name
+ klass, type, name = parse_name name
+
+ case type
+ when '#', '::' then
+ /^#{klass}#{type}#{Regexp.escape name}$/
+ else
+ /^#{klass}(#|::)#{Regexp.escape name}$/
+ end
+ end
+
+ ##
+ # Paginates output through a pager program.
+
+ def page
+ if pager = setup_pager then
+ begin
+ yield pager
+ ensure
+ pager.close
+ @jruby_pager_process.wait_for if @jruby_pager_process
+ end
+ else
+ yield $stdout
+ end
+ rescue Errno::EPIPE
+ ensure
+ @paging = false
+ end
+
+ ##
+ # Are we using a pager?
+
+ def paging?
+ @paging
+ end
+
+ ##
+ # Extracts the class, selector and method name parts from +name+ like
+ # Foo::Bar#baz.
+ #
+ # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
+ # method
+
+ def parse_name name
+ parts = name.split(/(::?|#|\.)/)
+
+ if parts.length == 1 then
+ if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then
+ type = '.'
+ meth = parts.pop
+ else
+ type = nil
+ meth = nil
+ end
+ elsif parts.length == 2 or parts.last =~ /::|#|\./ then
+ type = parts.pop
+ meth = nil
+ elsif parts[1] == ':' then
+ klass = parts.shift
+ type = parts.shift
+ meth = parts.join
+ elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
+ meth = parts.pop
+ type = parts.pop
+ end
+
+ klass ||= parts.join
+
+ [klass, type, meth]
+ end
+
+ ##
+ # Renders the +klass+ from +store+ to +out+. If the klass has no
+ # documentable items the class is added to +also_in+ instead.
+
+ def render_class out, store, klass, also_in # :nodoc:
+ comment = klass.comment
+ # TODO the store's cache should always return an empty Array
+ class_methods = store.class_methods[klass.full_name] || []
+ instance_methods = store.instance_methods[klass.full_name] || []
+ attributes = store.attributes[klass.full_name] || []
+
+ if comment.empty? and
+ instance_methods.empty? and class_methods.empty? then
+ also_in << store
+ return
+ end
+
+ add_from out, store
+
+ class_document_comment out, comment
+
+ if class_methods or instance_methods or not klass.constants.empty? then
+ out << RDoc::Markup::Rule.new(1)
+ end
+
+ class_document_constants out, klass
+
+ add_method_list out, class_methods, 'Class methods'
+ add_method_list out, instance_methods, 'Instance methods'
+ add_method_list out, attributes, 'Attributes'
+
+ add_method_documentation out, klass if @show_all
+ end
+
+ def render_method out, store, method, name # :nodoc:
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
+
+ unless name =~ /^#{Regexp.escape method.parent_name}/ then
+ out << RDoc::Markup::Heading.new(3, "Implementation from #{method.parent_name}")
+ end
+
+ out << RDoc::Markup::Rule.new(1)
+
+ render_method_arguments out, method.arglists
+ render_method_superclass out, method
+ render_method_comment out, method
+ end
+
+ def render_method_arguments out, arglists # :nodoc:
+ return unless arglists
+
+ arglists = arglists.chomp.split "\n"
+ arglists = arglists.map { |line| line + "\n" }
+ out << RDoc::Markup::Verbatim.new(*arglists)
+ out << RDoc::Markup::Rule.new(1)
+ end
+
+ def render_method_comment out, method # :nodoc:
+ out << RDoc::Markup::BlankLine.new
+ out << method.comment
+ out << RDoc::Markup::BlankLine.new
+ end
+
+ def render_method_superclass out, method # :nodoc:
+ return unless
+ method.respond_to?(:superclass_method) and method.superclass_method
+
+ out << RDoc::Markup::BlankLine.new
+ out << RDoc::Markup::Heading.new(4, "(Uses superclass method #{method.superclass_method})")
+ out << RDoc::Markup::Rule.new(1)
+ end
+
+ ##
+ # Looks up and displays ri data according to the options given.
+
+ def run
+ if @list_doc_dirs then
+ puts @doc_dirs
+ elsif @list then
+ list_known_classes @names
+ elsif @server then
+ start_server
+ elsif @interactive or @names.empty? then
+ interactive
+ else
+ display_names @names
+ end
+ rescue NotFoundError => e
+ abort e.message
+ end
+
+ ##
+ # Sets up a pager program to pass output through. Tries the RI_PAGER and
+ # PAGER environment variables followed by pager, less then more.
+
+ def setup_pager
+ return if @use_stdout
+
+ jruby = RUBY_ENGINE == 'jruby'
+
+ pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
+
+ pagers.compact.uniq.each do |pager|
+ next unless pager
+
+ pager_cmd = pager.split.first
+
+ next unless in_path? pager_cmd
+
+ if jruby then
+ case io = find_pager_jruby(pager)
+ when nil then break
+ when false then next
+ else io
+ end
+ else
+ io = IO.popen(pager, 'w') rescue next
+ end
+
+ next if $? and $?.pid == io.pid and $?.exited? # pager didn't work
+
+ @paging = true
+
+ return io
+ end
+
+ @use_stdout = true
+
+ nil
+ end
+
+ ##
+ # Starts a WEBrick server for ri.
+
+ def start_server
+ require 'webrick'
+
+ server = WEBrick::HTTPServer.new :Port => @server
+
+ extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact
+
+ server.mount '/', RDoc::Servlet, nil, extra_doc_dirs
+
+ trap 'INT' do server.shutdown end
+ trap 'TERM' do server.shutdown end
+
+ server.start
+ end
+
+end
diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb
new file mode 100644
index 0000000000..832a101e6c
--- /dev/null
+++ b/lib/rdoc/ri/formatter.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+##
+# For RubyGems backwards compatibility
+
+module RDoc::RI::Formatter # :nodoc:
+end
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
new file mode 100644
index 0000000000..d41e610591
--- /dev/null
+++ b/lib/rdoc/ri/paths.rb
@@ -0,0 +1,185 @@
+# frozen_string_literal: true
+require 'rdoc/ri'
+
+##
+# The directories where ri data lives. Paths can be enumerated via ::each, or
+# queried individually via ::system_dir, ::site_dir, ::home_dir and ::gem_dir.
+
+module RDoc::RI::Paths
+
+ #:stopdoc:
+ require 'rbconfig'
+
+ version = RbConfig::CONFIG['ruby_version']
+
+ BASE = if RbConfig::CONFIG.key? 'ridir' then
+ File.join RbConfig::CONFIG['ridir'], version
+ else
+ File.join RbConfig::CONFIG['datadir'], 'ri', version
+ end
+
+ homedir = begin
+ File.expand_path('~')
+ rescue ArgumentError
+ end
+
+ homedir ||= ENV['HOME'] ||
+ ENV['USERPROFILE'] || ENV['HOMEPATH'] # for 1.8 compatibility
+
+ HOMEDIR = if homedir then
+ File.join homedir, ".rdoc"
+ end
+ #:startdoc:
+
+ ##
+ # Iterates over each selected path yielding the directory and type.
+ #
+ # Yielded types:
+ # :system:: Where Ruby's ri data is stored. Yielded when +system+ is
+ # true
+ # :site:: Where ri for installed libraries are stored. Yielded when
+ # +site+ is true. Normally no ri data is stored here.
+ # :home:: ~/.rdoc. Yielded when +home+ is true.
+ # :gem:: ri data for an installed gem. Yielded when +gems+ is true.
+ # :extra:: ri data directory from the command line. Yielded for each
+ # entry in +extra_dirs+
+
+ def self.each system = true, site = true, home = true, gems = :latest, *extra_dirs # :yields: directory, type
+ return enum_for __method__, system, site, home, gems, *extra_dirs unless
+ block_given?
+
+ extra_dirs.each do |dir|
+ yield dir, :extra
+ end
+
+ yield system_dir, :system if system
+ yield site_dir, :site if site
+ yield home_dir, :home if home and HOMEDIR
+
+ gemdirs(gems).each do |dir|
+ yield dir, :gem
+ end if gems
+
+ nil
+ end
+
+ ##
+ # The ri directory for the gem with +gem_name+.
+
+ def self.gem_dir name, version
+ req = Gem::Requirement.new "= #{version}"
+
+ spec = Gem::Specification.find_by_name name, req
+
+ File.join spec.doc_dir, 'ri'
+ end
+
+ ##
+ # The latest installed gems' ri directories. +filter+ can be :all or
+ # :latest.
+ #
+ # A +filter+ :all includes all versions of gems and includes gems without
+ # ri documentation.
+
+ def self.gemdirs filter = :latest
+ ri_paths = {}
+
+ all = Gem::Specification.map do |spec|
+ [File.join(spec.doc_dir, 'ri'), spec.name, spec.version]
+ end
+
+ if filter == :all then
+ gemdirs = []
+
+ all.group_by do |_, name, _|
+ name
+ end.sort_by do |group, _|
+ group
+ end.map do |group, items|
+ items.sort_by do |_, _, version|
+ version
+ end.reverse_each do |dir,|
+ gemdirs << dir
+ end
+ end
+
+ return gemdirs
+ end
+
+ all.each do |dir, name, ver|
+ next unless File.exist? dir
+
+ if ri_paths[name].nil? or ver > ri_paths[name].first then
+ ri_paths[name] = [ver, name, dir]
+ end
+ end
+
+ ri_paths.sort_by { |_, (_, name, _)| name }.map { |k, v| v.last }
+ rescue LoadError
+ []
+ end
+
+ ##
+ # The location of the rdoc data in the user's home directory.
+ #
+ # Like ::system, ri data in the user's home directory is rare and predates
+ # libraries distributed via RubyGems. ri data is rarely generated into this
+ # directory.
+
+ def self.home_dir
+ HOMEDIR
+ end
+
+ ##
+ # Returns existing directories from the selected documentation directories
+ # as an Array.
+ #
+ # See also ::each
+
+ def self.path(system = true, site = true, home = true, gems = :latest, *extra_dirs)
+ path = raw_path system, site, home, gems, *extra_dirs
+
+ path.select { |directory| File.directory? directory }
+ end
+
+ ##
+ # Returns selected documentation directories including nonexistent
+ # directories.
+ #
+ # See also ::each
+
+ def self.raw_path(system, site, home, gems, *extra_dirs)
+ path = []
+
+ each(system, site, home, gems, *extra_dirs) do |dir, type|
+ path << dir
+ end
+
+ path.compact
+ end
+
+ ##
+ # The location of ri data installed into the site dir.
+ #
+ # Historically this was available for documentation installed by Ruby
+ # libraries predating RubyGems. It is unlikely to contain any content for
+ # modern Ruby installations.
+
+ def self.site_dir
+ File.join BASE, 'site'
+ end
+
+ ##
+ # The location of the built-in ri data.
+ #
+ # This data is built automatically when `make` is run when Ruby is
+ # installed. If you did not install Ruby by hand you may need to install
+ # the documentation yourself. Please consult the documentation for your
+ # package manager or Ruby installer for details. You can also use the
+ # rdoc-data gem to install system ri data for common versions of Ruby.
+
+ def self.system_dir
+ File.join BASE, 'system'
+ end
+
+end
diff --git a/lib/rdoc/ri/ri_cache.rb b/lib/rdoc/ri/ri_cache.rb
deleted file mode 100644
index 1844ac969e..0000000000
--- a/lib/rdoc/ri/ri_cache.rb
+++ /dev/null
@@ -1,187 +0,0 @@
-module RI
-
- class ClassEntry
-
- attr_reader :name
- attr_reader :path_names
-
- def initialize(path_name, name, in_class)
- @path_names = [ path_name ]
- @name = name
- @in_class = in_class
- @class_methods = []
- @instance_methods = []
- @inferior_classes = []
- end
-
- # We found this class in more tha one place, so add
- # in the name from there.
- def add_path(path)
- @path_names << path
- end
-
- # read in our methods and any classes
- # and modules in our namespace. Methods are
- # stored in files called name-c|i.yaml,
- # where the 'name' portion is the external
- # form of the method name and the c|i is a class|instance
- # flag
-
- def load_from(dir)
- Dir.foreach(dir) do |name|
- next if name =~ /^\./
-
- # convert from external to internal form, and
- # extract the instance/class flag
-
- if name =~ /^(.*?)-(c|i).yaml$/
- external_name = $1
- is_class_method = $2 == "c"
- internal_name = RiWriter.external_to_internal(external_name)
- list = is_class_method ? @class_methods : @instance_methods
- path = File.join(dir, name)
- list << MethodEntry.new(path, internal_name, is_class_method, self)
- else
- full_name = File.join(dir, name)
- if File.directory?(full_name)
- inf_class = @inferior_classes.find {|c| c.name == name }
- if inf_class
- inf_class.add_path(full_name)
- else
- inf_class = ClassEntry.new(full_name, name, self)
- @inferior_classes << inf_class
- end
- inf_class.load_from(full_name)
- end
- end
- end
- end
-
- # Return a list of any classes or modules that we contain
- # that match a given string
-
- def contained_modules_matching(name)
- @inferior_classes.find_all {|c| c.name[name]}
- end
-
- def classes_and_modules
- @inferior_classes
- end
-
- # Return an exact match to a particular name
- def contained_class_named(name)
- @inferior_classes.find {|c| c.name == name}
- end
-
- # return the list of local methods matching name
- # We're split into two because we need distinct behavior
- # when called from the _toplevel_
- def methods_matching(name, is_class_method)
- local_methods_matching(name, is_class_method)
- end
-
- # Find methods matching 'name' in ourselves and in
- # any classes we contain
- def recursively_find_methods_matching(name, is_class_method)
- res = local_methods_matching(name, is_class_method)
- @inferior_classes.each do |c|
- res.concat(c.recursively_find_methods_matching(name, is_class_method))
- end
- res
- end
-
-
- # Return our full name
- def full_name
- res = @in_class.full_name
- res << "::" unless res.empty?
- res << @name
- end
-
- # Return a list of all out method names
- def all_method_names
- res = @class_methods.map {|m| m.full_name }
- @instance_methods.each {|m| res << m.full_name}
- res
- end
-
- private
-
- # Return a list of all our methods matching a given string.
- # Is +is_class_methods+ if 'nil', we don't care if the method
- # is a class method or not, otherwise we only return
- # those methods that match
- def local_methods_matching(name, is_class_method)
-
- list = case is_class_method
- when nil then @class_methods + @instance_methods
- when true then @class_methods
- when false then @instance_methods
- else fail "Unknown is_class_method: #{is_class_method.inspect}"
- end
-
- list.find_all {|m| m.name; m.name[name]}
- end
- end
-
- # A TopLevelEntry is like a class entry, but when asked to search
- # for methods searches all classes, not just itself
-
- class TopLevelEntry < ClassEntry
- def methods_matching(name, is_class_method)
- res = recursively_find_methods_matching(name, is_class_method)
- end
-
- def full_name
- ""
- end
-
- def module_named(name)
-
- end
-
- end
-
- class MethodEntry
- attr_reader :name
- attr_reader :path_name
-
- def initialize(path_name, name, is_class_method, in_class)
- @path_name = path_name
- @name = name
- @is_class_method = is_class_method
- @in_class = in_class
- end
-
- def full_name
- res = @in_class.full_name
- unless res.empty?
- if @is_class_method
- res << "::"
- else
- res << "#"
- end
- end
- res << @name
- end
- end
-
- # We represent everything know about all 'ri' files
- # accessible to this program
-
- class RiCache
-
- attr_reader :toplevel
-
- def initialize(dirs)
- # At the top level we have a dummy module holding the
- # overall namespace
- @toplevel = TopLevelEntry.new('', '::', nil)
-
- dirs.each do |dir|
- @toplevel.load_from(dir)
- end
- end
-
- end
-end
diff --git a/lib/rdoc/ri/ri_descriptions.rb b/lib/rdoc/ri/ri_descriptions.rb
deleted file mode 100644
index e5ea9f2fbf..0000000000
--- a/lib/rdoc/ri/ri_descriptions.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-require 'yaml'
-require 'rdoc/markup/simple_markup/fragments'
-
-# Descriptions are created by RDoc (in ri_generator) and
-# written out in serialized form into the documentation
-# tree. ri then reads these to generate the documentation
-
-module RI
- class NamedThing
- attr_reader :name
- def initialize(name)
- @name = name
- end
- def <=>(other)
- @name <=> other.name
- end
-
- def hash
- @name.hash
- end
-
- def eql?(other)
- @name.eql?(other)
- end
- end
-
-# Alias = Struct.new(:old_name, :new_name)
-
- class AliasName < NamedThing
- end
-
- class Attribute < NamedThing
- attr_reader :rw, :comment
- def initialize(name, rw, comment)
- super(name)
- @rw = rw
- @comment = comment
- end
- end
-
- class Constant < NamedThing
- attr_reader :value, :comment
- def initialize(name, value, comment)
- super(name)
- @value = value
- @comment = comment
- end
- end
-
- class IncludedModule < NamedThing
- end
-
-
- class MethodSummary < NamedThing
- def initialize(name="")
- super
- end
- end
-
-
-
- class Description
- attr_accessor :name
- attr_accessor :full_name
- attr_accessor :comment
-
- def serialize
- self.to_yaml
- end
-
- def Description.deserialize(from)
- YAML.load(from)
- end
-
- def <=>(other)
- @name <=> other.name
- end
- end
-
- class ModuleDescription < Description
-
- attr_accessor :class_methods
- attr_accessor :instance_methods
- attr_accessor :attributes
- attr_accessor :constants
- attr_accessor :includes
-
- # merge in another class desscription into this one
- def merge_in(old)
- merge(@class_methods, old.class_methods)
- merge(@instance_methods, old.instance_methods)
- merge(@attributes, old.attributes)
- merge(@constants, old.constants)
- merge(@includes, old.includes)
- if @comment.nil? || @comment.empty?
- @comment = old.comment
- else
- unless old.comment.nil? or old.comment.empty? then
- @comment << SM::Flow::RULE.new
- @comment.concat old.comment
- end
- end
- end
-
- def display_name
- "Module"
- end
-
- # the 'ClassDescription' subclass overrides this
- # to format up the name of a parent
- def superclass_string
- nil
- end
-
- private
-
- def merge(into, from)
- names = {}
- into.each {|i| names[i.name] = i }
- from.each {|i| names[i.name] = i }
- into.replace(names.keys.sort.map {|n| names[n]})
- end
- end
-
- class ClassDescription < ModuleDescription
- attr_accessor :superclass
-
- def display_name
- "Class"
- end
-
- def superclass_string
- if @superclass && @superclass != "Object"
- @superclass
- else
- nil
- end
- end
- end
-
-
- class MethodDescription < Description
-
- attr_accessor :is_class_method
- attr_accessor :visibility
- attr_accessor :block_params
- attr_accessor :is_singleton
- attr_accessor :aliases
- attr_accessor :is_alias_for
- attr_accessor :params
-
- end
-
-end
diff --git a/lib/rdoc/ri/ri_display.rb b/lib/rdoc/ri/ri_display.rb
deleted file mode 100644
index 67962fc2c1..0000000000
--- a/lib/rdoc/ri/ri_display.rb
+++ /dev/null
@@ -1,255 +0,0 @@
-require 'rdoc/ri/ri_util'
-require 'rdoc/ri/ri_formatter'
-require 'rdoc/ri/ri_options'
-
-
-# This is a kind of 'flag' module. If you want to write your
-# own 'ri' display module (perhaps because you'r writing
-# an IDE or somesuch beast), you simply write a class
-# which implements the various 'display' methods in 'DefaultDisplay',
-# and include the 'RiDisplay' module in that class.
-#
-# To access your class from the command line, you can do
-#
-# ruby -r <your source file> ../ri ....
-#
-# If folks _really_ want to do this from the command line,
-# I'll build an option in
-
-module RiDisplay
- @@display_class = nil
-
- def RiDisplay.append_features(display_class)
- @@display_class = display_class
- end
-
- def RiDisplay.new(*args)
- @@display_class.new(*args)
- end
-end
-
-######################################################################
-#
-# A paging display module. Uses the ri_formatter class to do the
-# actual presentation
-#
-
-class DefaultDisplay
-
- include RiDisplay
-
- def initialize(options)
- @options = options
- @formatter = @options.formatter.new(@options, " ")
- end
-
-
- ######################################################################
-
- def display_usage
- page do
- RI::Options::OptionList.usage(short_form=true)
- end
- end
-
-
- ######################################################################
-
- def display_method_info(method)
- page do
- @formatter.draw_line(method.full_name)
- display_params(method)
- @formatter.draw_line
- display_flow(method.comment)
- if method.aliases && !method.aliases.empty?
- @formatter.blankline
- aka = "(also known as "
- aka << method.aliases.map {|a| a.name }.join(", ")
- aka << ")"
- @formatter.wrap(aka)
- end
- end
- end
-
- ######################################################################
-
- def display_class_info(klass, ri_reader)
- page do
- superclass = klass.superclass_string
-
- if superclass
- superclass = " < " + superclass
- else
- superclass = ""
- end
-
- @formatter.draw_line(klass.display_name + ": " +
- klass.full_name + superclass)
-
- display_flow(klass.comment)
- @formatter.draw_line
-
- unless klass.includes.empty?
- @formatter.blankline
- @formatter.display_heading("Includes:", 2, "")
- incs = []
- klass.includes.each do |inc|
- inc_desc = ri_reader.find_class_by_name(inc.name)
- if inc_desc
- str = inc.name + "("
- str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
- str << ")"
- incs << str
- else
- incs << inc.name
- end
- end
- @formatter.wrap(incs.sort.join(', '))
- end
-
- unless klass.constants.empty?
- @formatter.blankline
- @formatter.display_heading("Constants:", 2, "")
- len = 0
- klass.constants.each { |c| len = c.name.length if c.name.length > len }
- len += 2
- klass.constants.each do |c|
- @formatter.wrap(c.value,
- @formatter.indent+((c.name+":").ljust(len)))
- end
- end
-
- unless klass.class_methods.empty?
- @formatter.blankline
- @formatter.display_heading("Class methods:", 2, "")
- @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', '))
- end
-
- unless klass.instance_methods.empty?
- @formatter.blankline
- @formatter.display_heading("Instance methods:", 2, "")
- @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', '))
- end
-
- unless klass.attributes.empty?
- @formatter.blankline
- @formatter.wrap("Attributes:", "")
- @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', '))
- end
- end
- end
-
- ######################################################################
-
- # Display a list of method names
-
- def display_method_list(methods)
- page do
- puts "More than one method matched your request. You can refine"
- puts "your search by asking for information on one of:\n\n"
- @formatter.wrap(methods.map {|m| m.full_name} .join(", "))
- end
- end
-
- ######################################################################
-
- def display_class_list(namespaces)
- page do
- puts "More than one class or module matched your request. You can refine"
- puts "your search by asking for information on one of:\n\n"
- @formatter.wrap(namespaces.map {|m| m.full_name}.join(", "))
- end
- end
-
- ######################################################################
-
- def list_known_classes(classes)
- if classes.empty?
- warn_no_database
- else
- page do
- @formatter.draw_line("Known classes and modules")
- @formatter.blankline
- @formatter.wrap(classes.sort.join(", "))
- end
- end
- end
-
- ######################################################################
-
- def list_known_names(names)
- if names.empty?
- warn_no_database
- else
- page do
- names.each {|n| @formatter.raw_print_line(n)}
- end
- end
- end
-
- ######################################################################
-
- private
-
- ######################################################################
-
- def page
- return yield unless pager = setup_pager
- begin
- save_stdout = STDOUT.clone
- STDOUT.reopen(pager)
- yield
- ensure
- STDOUT.reopen(save_stdout)
- save_stdout.close
- pager.close
- end
- end
-
- ######################################################################
-
- def setup_pager
- unless @options.use_stdout
- for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq
- return IO.popen(pager, "w") rescue nil
- end
- @options.use_stdout = true
- nil
- end
- end
-
- ######################################################################
-
- def display_params(method)
-
- params = method.params
-
- if params[0,1] == "("
- if method.is_singleton
- params = method.full_name + params
- else
- params = method.name + params
- end
- end
- params.split(/\n/).each do |p|
- @formatter.wrap(p)
- @formatter.break_to_newline
- end
- end
- ######################################################################
-
- def display_flow(flow)
- if !flow || flow.empty?
- @formatter.wrap("(no description...)")
- else
- @formatter.display_flow(flow)
- end
- end
-
- ######################################################################
-
- def warn_no_database
- puts "Before using ri, you need to generate documentation"
- puts "using 'rdoc' with the --ri option"
- end
-end # class RiDisplay
diff --git a/lib/rdoc/ri/ri_driver.rb b/lib/rdoc/ri/ri_driver.rb
deleted file mode 100644
index a00f20ee3b..0000000000
--- a/lib/rdoc/ri/ri_driver.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-require 'rdoc/ri/ri_paths'
-require 'rdoc/usage'
-require 'rdoc/ri/ri_cache'
-require 'rdoc/ri/ri_util'
-require 'rdoc/ri/ri_reader'
-require 'rdoc/ri/ri_formatter'
-require 'rdoc/ri/ri_options'
-
-
-######################################################################
-
-class RiDriver
-
- def initialize
- @options = RI::Options.instance
-
- args = ARGV
- if ENV["RI"]
- args = ENV["RI"].split.concat(ARGV)
- end
-
- @options.parse(args)
-
- path = @options.path
- report_missing_documentation @options.raw_path if path.empty?
-
- @ri_reader = RI::RiReader.new(RI::RiCache.new(path))
- @display = @options.displayer
- end
-
- # Couldn't find documentation in +path+, so tell the user what to do
-
- def report_missing_documentation(path)
- STDERR.puts "No ri documentation found in:"
- path.each do |d|
- STDERR.puts " #{d}"
- end
- STDERR.puts "\nWas rdoc run to create documentation?\n\n"
- RDoc::usage("Installing Documentation")
- end
-
- ######################################################################
-
- # If the list of matching methods contains exactly one entry, or
- # if it contains an entry that exactly matches the requested method,
- # then display that entry, otherwise display the list of
- # matching method names
-
- def report_method_stuff(requested_method_name, methods)
- if methods.size == 1
- method = @ri_reader.get_method(methods[0])
- @display.display_method_info(method)
- else
- entries = methods.find_all {|m| m.name == requested_method_name}
- if entries.size == 1
- method = @ri_reader.get_method(entries[0])
- @display.display_method_info(method)
- else
- @display.display_method_list(methods)
- end
- end
- end
-
- ######################################################################
-
- def report_class_stuff(namespaces)
- if namespaces.size == 1
- klass = @ri_reader.get_class(namespaces[0])
- @display.display_class_info(klass, @ri_reader)
- else
-# entries = namespaces.find_all {|m| m.full_name == requested_class_name}
-# if entries.size == 1
-# klass = @ri_reader.get_class(entries[0])
-# @display.display_class_info(klass, @ri_reader)
-# else
- @display.display_class_list(namespaces)
-# end
- end
- end
-
- ######################################################################
-
-
- def get_info_for(arg)
- desc = NameDescriptor.new(arg)
-
- namespaces = @ri_reader.top_level_namespace
-
- for class_name in desc.class_names
- namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
- if namespaces.empty?
- raise RiError.new("Nothing known about #{arg}")
- end
- end
-
- # at this point, if we have multiple possible namespaces, but one
- # is an exact match for our requested class, prune down to just it
-
- full_class_name = desc.full_class_name
- entries = namespaces.find_all {|m| m.full_name == full_class_name}
- namespaces = entries if entries.size == 1
-
- if desc.method_name.nil?
- report_class_stuff(namespaces)
- else
- methods = @ri_reader.find_methods(desc.method_name,
- desc.is_class_method,
- namespaces)
-
- if methods.empty?
- raise RiError.new("Nothing known about #{arg}")
- else
- report_method_stuff(desc.method_name, methods)
- end
- end
- end
-
- ######################################################################
-
- def process_args
- if @options.list_classes
- classes = @ri_reader.full_class_names
- @display.list_known_classes(classes)
- elsif @options.list_names
- names = @ri_reader.all_names
- @display.list_known_names(names)
- else
- if ARGV.size.zero?
- @display.display_usage
- else
- begin
- ARGV.each do |arg|
- get_info_for(arg)
- end
- rescue RiError => e
- STDERR.puts(e.message)
- exit(1)
- end
- end
- end
- end
-
-end # class RiDriver
diff --git a/lib/rdoc/ri/ri_formatter.rb b/lib/rdoc/ri/ri_formatter.rb
deleted file mode 100644
index 56a1fb4665..0000000000
--- a/lib/rdoc/ri/ri_formatter.rb
+++ /dev/null
@@ -1,674 +0,0 @@
-module RI
- class TextFormatter
-
- attr_reader :indent
-
- def initialize(options, indent)
- @options = options
- @width = options.width
- @indent = indent
- end
-
-
- ######################################################################
-
- def draw_line(label=nil)
- len = @width
- len -= (label.size+1) if label
- print "-"*len
- if label
- print(" ")
- bold_print(label)
- end
- puts
- end
-
- ######################################################################
-
- def wrap(txt, prefix=@indent, linelen=@width)
- return unless txt && !txt.empty?
- work = conv_markup(txt)
- textLen = linelen - prefix.length
- patt = Regexp.new("^(.{0,#{textLen}})[ \n]")
- next_prefix = prefix.tr("^ ", " ")
-
- res = []
-
- while work.length > textLen
- if work =~ patt
- res << $1
- work.slice!(0, $&.length)
- else
- res << work.slice!(0, textLen)
- end
- end
- res << work if work.length.nonzero?
- puts(prefix + res.join("\n" + next_prefix))
- end
-
- ######################################################################
-
- def blankline
- puts
- end
-
- ######################################################################
-
- # called when we want to ensure a nbew 'wrap' starts on a newline
- # Only needed for HtmlFormatter, because the rest do their
- # own line breaking
-
- def break_to_newline
- end
-
- ######################################################################
-
- def bold_print(txt)
- print txt
- end
-
- ######################################################################
-
- def raw_print_line(txt)
- puts txt
- end
-
- ######################################################################
-
- # convert HTML entities back to ASCII
- def conv_html(txt)
- txt.
- gsub(/&gt;/, '>').
- gsub(/&lt;/, '<').
- gsub(/&quot;/, '"').
- gsub(/&amp;/, '&')
-
- end
-
- # convert markup into display form
- def conv_markup(txt)
- txt.
- gsub(%r{<tt>(.*?)</tt>}) { "+#$1+" } .
- gsub(%r{<code>(.*?)</code>}) { "+#$1+" } .
- gsub(%r{<b>(.*?)</b>}) { "*#$1*" } .
- gsub(%r{<em>(.*?)</em>}) { "_#$1_" }
- end
-
- ######################################################################
-
- def display_list(list)
- case list.type
-
- when SM::ListBase::BULLET
- prefixer = proc { |ignored| @indent + "* " }
-
- when SM::ListBase::NUMBER,
- SM::ListBase::UPPERALPHA,
- SM::ListBase::LOWERALPHA
-
- start = case list.type
- when SM::ListBase::NUMBER then 1
- when SM::ListBase::UPPERALPHA then 'A'
- when SM::ListBase::LOWERALPHA then 'a'
- end
- prefixer = proc do |ignored|
- res = @indent + "#{start}.".ljust(4)
- start = start.succ
- res
- end
-
- when SM::ListBase::LABELED
- prefixer = proc do |li|
- li.label
- end
-
- when SM::ListBase::NOTE
- longest = 0
- list.contents.each do |item|
- if item.kind_of?(SM::Flow::LI) && item.label.length > longest
- longest = item.label.length
- end
- end
-
- prefixer = proc do |li|
- @indent + li.label.ljust(longest+1)
- end
-
- else
- fail "unknown list type"
-
- end
-
- list.contents.each do |item|
- if item.kind_of? SM::Flow::LI
- prefix = prefixer.call(item)
- display_flow_item(item, prefix)
- else
- display_flow_item(item)
- end
- end
- end
-
- ######################################################################
-
- def display_flow_item(item, prefix=@indent)
- case item
- when SM::Flow::P, SM::Flow::LI
- wrap(conv_html(item.body), prefix)
- blankline
-
- when SM::Flow::LIST
- display_list(item)
-
- when SM::Flow::VERB
- display_verbatim_flow_item(item, @indent)
-
- when SM::Flow::H
- display_heading(conv_html(item.text), item.level, @indent)
-
- when SM::Flow::RULE
- draw_line
-
- else
- fail "Unknown flow element: #{item.class}"
- end
- end
-
- ######################################################################
-
- def display_verbatim_flow_item(item, prefix=@indent)
- item.body.split(/\n/).each do |line|
- print @indent, conv_html(line), "\n"
- end
- blankline
- end
-
- ######################################################################
-
- def display_heading(text, level, indent)
- text = strip_attributes(text)
- case level
- when 1
- ul = "=" * text.length
- puts
- puts text.upcase
- puts ul
-# puts
-
- when 2
- ul = "-" * text.length
- puts
- puts text
- puts ul
-# puts
- else
- print indent, text, "\n"
- end
- end
-
-
- def display_flow(flow)
- flow.each do |f|
- display_flow_item(f)
- end
- end
-
- def strip_attributes(txt)
- tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
- text = []
- attributes = 0
- tokens.each do |tok|
- case tok
- when %r{^</(\w+)>$}, %r{^<(\w+)>$}
- ;
- else
- text << tok
- end
- end
- text.join
- end
-
-
- end
-
-
- ######################################################################
- # Handle text with attributes. We're a base class: there are
- # different presentation classes (one, for example, uses overstrikes
- # to handle bold and underlining, while another using ANSI escape
- # sequences
-
- class AttributeFormatter < TextFormatter
-
- BOLD = 1
- ITALIC = 2
- CODE = 4
-
- ATTR_MAP = {
- "b" => BOLD,
- "code" => CODE,
- "em" => ITALIC,
- "i" => ITALIC,
- "tt" => CODE
- }
-
- # TODO: struct?
- class AttrChar
- attr_reader :char
- attr_reader :attr
-
- def initialize(char, attr)
- @char = char
- @attr = attr
- end
- end
-
-
- class AttributeString
- attr_reader :txt
-
- def initialize
- @txt = []
- @optr = 0
- end
-
- def <<(char)
- @txt << char
- end
-
- def empty?
- @optr >= @txt.length
- end
-
- # accept non space, then all following spaces
- def next_word
- start = @optr
- len = @txt.length
-
- while @optr < len && @txt[@optr].char != " "
- @optr += 1
- end
-
- while @optr < len && @txt[@optr].char == " "
- @optr += 1
- end
-
- @txt[start...@optr]
- end
- end
-
- ######################################################################
- # overrides base class. Looks for <tt>...</tt> etc sequences
- # and generates an array of AttrChars. This array is then used
- # as the basis for the split
-
- def wrap(txt, prefix=@indent, linelen=@width)
- return unless txt && !txt.empty?
-
- txt = add_attributes_to(txt)
- next_prefix = prefix.tr("^ ", " ")
- linelen -= prefix.size
-
- line = []
-
- until txt.empty?
- word = txt.next_word
- if word.size + line.size > linelen
- write_attribute_text(prefix, line)
- prefix = next_prefix
- line = []
- end
- line.concat(word)
- end
-
- write_attribute_text(prefix, line) if line.length > 0
- end
-
- protected
-
- # overridden in specific formatters
-
- def write_attribute_text(prefix, line)
- print prefix
- line.each do |achar|
- print achar.char
- end
- puts
- end
-
- # again, overridden
-
- def bold_print(txt)
- print txt
- end
-
- private
-
- def add_attributes_to(txt)
- tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
- text = AttributeString.new
- attributes = 0
- tokens.each do |tok|
- case tok
- when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0)
- when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0)
- else
- tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)}
- end
- end
- text
- end
-
- end
-
-
- ##################################################
-
- # This formatter generates overstrike-style formatting, which
- # works with pagers such as man and less.
-
- class OverstrikeFormatter < AttributeFormatter
-
- BS = "\C-h"
-
- def write_attribute_text(prefix, line)
- print prefix
- line.each do |achar|
- attr = achar.attr
- if (attr & (ITALIC+CODE)) != 0
- print "_", BS
- end
- if (attr & BOLD) != 0
- print achar.char, BS
- end
- print achar.char
- end
- puts
- end
-
- # draw a string in bold
- def bold_print(text)
- text.split(//).each do |ch|
- print ch, BS, ch
- end
- end
- end
-
- ##################################################
-
- # This formatter uses ANSI escape sequences
- # to colorize stuff
- # works with pages such as man and less.
-
- class AnsiFormatter < AttributeFormatter
-
- def initialize(*args)
- print "\033[0m"
- super
- end
-
- def write_attribute_text(prefix, line)
- print prefix
- curr_attr = 0
- line.each do |achar|
- attr = achar.attr
- if achar.attr != curr_attr
- update_attributes(achar.attr)
- curr_attr = achar.attr
- end
- print achar.char
- end
- update_attributes(0) unless curr_attr.zero?
- puts
- end
-
-
- def bold_print(txt)
- print "\033[1m#{txt}\033[m"
- end
-
- HEADINGS = {
- 1 => [ "\033[1;32m", "\033[m" ] ,
- 2 => ["\033[4;32m", "\033[m" ],
- 3 => ["\033[32m", "\033[m" ]
- }
-
- def display_heading(text, level, indent)
- level = 3 if level > 3
- heading = HEADINGS[level]
- print indent
- print heading[0]
- print strip_attributes(text)
- puts heading[1]
- end
-
- private
-
- ATTR_MAP = {
- BOLD => "1",
- ITALIC => "33",
- CODE => "36"
- }
-
- def update_attributes(attr)
- str = "\033["
- for quality in [ BOLD, ITALIC, CODE]
- unless (attr & quality).zero?
- str << ATTR_MAP[quality]
- end
- end
- print str, "m"
- end
- end
-
- ##################################################
-
- # This formatter uses HTML.
-
- class HtmlFormatter < AttributeFormatter
-
- def initialize(*args)
- super
- end
-
- def write_attribute_text(prefix, line)
- curr_attr = 0
- line.each do |achar|
- attr = achar.attr
- if achar.attr != curr_attr
- update_attributes(curr_attr, achar.attr)
- curr_attr = achar.attr
- end
- print(escape(achar.char))
- end
- update_attributes(curr_attr, 0) unless curr_attr.zero?
- end
-
- def draw_line(label=nil)
- if label != nil
- bold_print(label)
- end
- puts("<hr>")
- end
-
- def bold_print(txt)
- tag("b") { txt }
- end
-
- def blankline()
- puts("<p>")
- end
-
- def break_to_newline
- puts("<br>")
- end
-
- def display_heading(text, level, indent)
- level = 4 if level > 4
- tag("h#{level}") { text }
- puts
- end
-
- ######################################################################
-
- def display_list(list)
-
- case list.type
- when SM::ListBase::BULLET
- list_type = "ul"
- prefixer = proc { |ignored| "<li>" }
-
- when SM::ListBase::NUMBER,
- SM::ListBase::UPPERALPHA,
- SM::ListBase::LOWERALPHA
- list_type = "ol"
- prefixer = proc { |ignored| "<li>" }
-
- when SM::ListBase::LABELED
- list_type = "dl"
- prefixer = proc do |li|
- "<dt><b>" + escape(li.label) + "</b><dd>"
- end
-
- when SM::ListBase::NOTE
- list_type = "table"
- prefixer = proc do |li|
- %{<tr valign="top"><td>#{li.label.gsub(/ /, '&nbsp;')}</td><td>}
- end
- else
- fail "unknown list type"
- end
-
- print "<#{list_type}>"
- list.contents.each do |item|
- if item.kind_of? SM::Flow::LI
- prefix = prefixer.call(item)
- print prefix
- display_flow_item(item, prefix)
- else
- display_flow_item(item)
- end
- end
- print "</#{list_type}>"
- end
-
- def display_verbatim_flow_item(item, prefix=@indent)
- print("<pre>")
- item.body.split(/\n/).each do |line|
- puts conv_html(line)
- end
- puts("</pre>")
- end
-
- private
-
- ATTR_MAP = {
- BOLD => "b>",
- ITALIC => "i>",
- CODE => "tt>"
- }
-
- def update_attributes(current, wanted)
- str = ""
- # first turn off unwanted ones
- off = current & ~wanted
- for quality in [ BOLD, ITALIC, CODE]
- if (off & quality) > 0
- str << "</" + ATTR_MAP[quality]
- end
- end
-
- # now turn on wanted
- for quality in [ BOLD, ITALIC, CODE]
- unless (wanted & quality).zero?
- str << "<" << ATTR_MAP[quality]
- end
- end
- print str
- end
-
- def tag(code)
- print("<#{code}>")
- print(yield)
- print("</#{code}>")
- end
-
- def escape(str)
- str.
- gsub(/&/n, '&amp;').
- gsub(/\"/n, '&quot;').
- gsub(/>/n, '&gt;').
- gsub(/</n, '&lt;')
- end
-
- end
-
- ##################################################
-
- # This formatter reduces extra lines for a simpler output.
- # It improves way output looks for tools like IRC bots.
-
- class SimpleFormatter < TextFormatter
-
- ######################################################################
-
- # No extra blank lines
-
- def blankline
- end
-
- ######################################################################
-
- # Display labels only, no lines
-
- def draw_line(label=nil)
- unless label.nil? then
- bold_print(label)
- puts
- end
- end
-
- ######################################################################
-
- # Place heading level indicators inline with heading.
-
- def display_heading(text, level, indent)
- text = strip_attributes(text)
- case level
- when 1
- puts "= " + text.upcase
- when 2
- puts "-- " + text
- else
- print indent, text, "\n"
- end
- end
-
- end
-
-
- # Finally, fill in the list of known formatters
-
- class TextFormatter
-
- FORMATTERS = {
- "ansi" => AnsiFormatter,
- "bs" => OverstrikeFormatter,
- "html" => HtmlFormatter,
- "plain" => TextFormatter,
- "simple" => SimpleFormatter,
- }
-
- def TextFormatter.list
- FORMATTERS.keys.sort.join(", ")
- end
-
- def TextFormatter.for(name)
- FORMATTERS[name.downcase]
- end
-
- end
-
-end
-
-
diff --git a/lib/rdoc/ri/ri_options.rb b/lib/rdoc/ri/ri_options.rb
deleted file mode 100644
index db9f4afecf..0000000000
--- a/lib/rdoc/ri/ri_options.rb
+++ /dev/null
@@ -1,313 +0,0 @@
-# We handle the parsing of options, and subsequently as a singleton
-# object to be queried for option values
-
-module RI
-
- require 'rdoc/ri/ri_paths'
- require 'rdoc/ri/ri_display'
-
- VERSION_STRING = "ri v1.0.1 - 20041108"
-
- class Options
-
- require 'singleton'
- require 'getoptlong'
-
- include Singleton
-
- # No not use a pager. Writable, because ri sets it if it
- # can't find a pager
- attr_accessor :use_stdout
-
- # should we just display a class list and exit
- attr_reader :list_classes
-
- # should we display a list of all names
- attr_reader :list_names
-
- # The width of the output line
- attr_reader :width
-
- # the formatting we apply to the output
- attr_reader :formatter
-
- # the directory we search for original documentation
- attr_reader :doc_dir
-
- module OptionList
-
- OPTION_LIST = [
- [ "--help", "-h", nil,
- "you're looking at it" ],
-
- [ "--classes", "-c", nil,
- "Display the names of classes and modules we\n" +
- "know about"],
-
- [ "--doc-dir", "-d", "<dirname>",
- "A directory to search for documentation. If not\n" +
- "specified, we search the standard rdoc/ri directories.\n" +
- "May be repeated."],
-
- [ "--system", nil, nil,
- "Include documentation from Ruby's standard library:\n " +
- RI::Paths::SYSDIR ],
-
- [ "--site", nil, nil,
- "Include documentation from libraries installed in site_lib:\n " +
- RI::Paths::SITEDIR ],
-
- [ "--home", nil, nil,
- "Include documentation stored in ~/.rdoc:\n " +
- (RI::Paths::HOMEDIR || "No ~/.rdoc found") ],
-
- [ "--gems", nil, nil,
- "Include documentation from Rubygems:\n " +
- (RI::Paths::GEMDIRS ? "#{Gem.path}/doc/*/ri" :
- "No Rubygems ri found.") ],
-
- [ "--format", "-f", "<name>",
- "Format to use when displaying output:\n" +
- " " + RI::TextFormatter.list + "\n" +
- "Use 'bs' (backspace) with most pager programs.\n" +
- "To use ANSI, either also use the -T option, or\n" +
- "tell your pager to allow control characters\n" +
- "(for example using the -R option to less)"],
-
- [ "--list-names", "-l", nil,
- "List all the names known to RDoc, one per line"
- ],
-
- [ "--no-pager", "-T", nil,
- "Send output directly to stdout."
- ],
-
- [ "--width", "-w", "output width",
- "Set the width of the output" ],
-
- [ "--version", "-v", nil,
- "Display the version of ri"
- ],
-
- ]
-
- def OptionList.options
- OPTION_LIST.map do |long, short, arg,|
- option = []
- option << long
- option << short unless short.nil?
- option << (arg ? GetoptLong::REQUIRED_ARGUMENT :
- GetoptLong::NO_ARGUMENT)
- option
- end
- end
-
-
- def OptionList.strip_output(text)
- text =~ /^\s+/
- leading_spaces = $&
- text.gsub!(/^#{leading_spaces}/, '')
- $stdout.puts text
- end
-
-
- # Show an error and exit
-
- def OptionList.error(msg)
- $stderr.puts
- $stderr.puts msg
- $stderr.puts "\nFor help on options, try 'ri --help'\n\n"
- exit 1
- end
-
- # Show usage and exit
-
- def OptionList.usage(short_form=false)
-
- puts
- puts(RI::VERSION_STRING)
- puts
-
- name = File.basename($0)
-
- directories = [
- RI::Paths::SYSDIR,
- RI::Paths::SITEDIR,
- RI::Paths::HOMEDIR
- ]
-
- directories << "#{Gem.path}/doc/*/ri" if RI::Paths::GEMDIRS
-
- directories = directories.join("\n ")
-
- OptionList.strip_output(<<-EOT)
- Usage:
-
- #{name} [options] [names...]
-
- Display information on Ruby classes, modules, and methods.
- Give the names of classes or methods to see their documentation.
- Partial names may be given: if the names match more than
- one entity, a list will be shown, otherwise details on
- that entity will be displayed.
-
- Nested classes and modules can be specified using the normal
- Name::Name notation, and instance methods can be distinguished
- from class methods using "." (or "#") instead of "::".
-
- For example:
-
- ri File
- ri File.new
- ri F.n
- ri zip
-
- Note that shell quoting may be required for method names
- containing punctuation:
-
- ri 'Array.[]'
- ri compact\\!
-
- By default ri searches for documentation in the following
- directories:
-
- #{directories}
-
- Specifying the --system, --site, --home, --gems or --doc-dir
- options will limit ri to searching only the specified
- directories.
-
- EOT
-
- if short_form
- puts "For help on options, type 'ri -h'"
- puts "For a list of classes I know about, type 'ri -c'"
- else
- puts "Options:\n\n"
- OPTION_LIST.each do|long, short, arg, desc|
- opt = ''
- opt << (short ? sprintf("%15s", "#{long}, #{short}") :
- sprintf("%15s", long))
- if arg
- opt << " " << arg
- end
- print opt
- desc = desc.split("\n")
- if opt.size < 17
- print " "*(18-opt.size)
- puts desc.shift
- else
- puts
- end
- desc.each do |line|
- puts(" "*18 + line)
- end
- puts
- end
- puts "Options may also be passed in the 'RI' environment variable"
- exit 0
- end
- end
- end
-
- # Show the version and exit
- def show_version
- puts VERSION_STRING
- exit(0)
- end
-
- def initialize
- @use_stdout = !STDOUT.tty?
- @width = 72
- @formatter = RI::TextFormatter.for("plain")
- @list_classes = false
- @list_names = false
-
- # By default all paths are used. If any of these are true, only those
- # directories are used.
- @use_system = false
- @use_site = false
- @use_home = false
- @use_gems = false
- @doc_dirs = []
- end
-
- # Parse command line options.
-
- def parse(args)
-
- old_argv = ARGV.dup
-
- ARGV.replace(args)
-
- begin
-
- go = GetoptLong.new(*OptionList.options)
- go.quiet = true
-
- go.each do |opt, arg|
- case opt
- when "--help" then OptionList.usage
- when "--version" then show_version
- when "--list-names" then @list_names = true
- when "--no-pager" then @use_stdout = true
- when "--classes" then @list_classes = true
-
- when "--system" then @use_system = true
- when "--site" then @use_site = true
- when "--home" then @use_home = true
- when "--gems" then @use_gems = true
-
- when "--doc-dir"
- if File.directory?(arg)
- @doc_dirs << arg
- else
- $stderr.puts "Invalid directory: #{arg}"
- exit 1
- end
-
- when "--format"
- @formatter = RI::TextFormatter.for(arg)
- unless @formatter
- $stderr.print "Invalid formatter (should be one of "
- $stderr.puts RI::TextFormatter.list + ")"
- exit 1
- end
- when "--width"
- begin
- @width = Integer(arg)
- rescue
- $stderr.puts "Invalid width: '#{arg}'"
- exit 1
- end
- end
- end
-
- rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error
- OptionList.error(error.message)
-
- end
- end
-
- # Return the selected documentation directories.
-
- def path
- RI::Paths.path(@use_system, @use_site, @use_home, @use_gems, *@doc_dirs)
- end
-
- def raw_path
- RI::Paths.raw_path(@use_system, @use_site, @use_home, @use_gems,
- *@doc_dirs)
- end
-
- # Return an instance of the displayer (the thing that actually writes
- # the information). This allows us to load in new displayer classes
- # at runtime (for example to help with IDE integration)
-
- def displayer
- ::RiDisplay.new(self)
- end
- end
-
-end
-
diff --git a/lib/rdoc/ri/ri_paths.rb b/lib/rdoc/ri/ri_paths.rb
deleted file mode 100644
index 32363bf70a..0000000000
--- a/lib/rdoc/ri/ri_paths.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-module RI
-
- # Encapsulate all the strangeness to do with finding out
- # where to find RDoc files
- #
- # We basically deal with three directories:
- #
- # 1. The 'system' documentation directory, which holds
- # the documentation distributed with Ruby, and which
- # is managed by the Ruby install process
- # 2. The 'site' directory, which contains site-wide
- # documentation added locally.
- # 3. The 'user' documentation directory, stored under the
- # user's own home directory.
- #
- # There's contention about all this, but for now:
- #
- # system:: $datadir/ri/<ver>/system/...
- # site:: $datadir/ri/<ver>/site/...
- # user:: ~/.rdoc
-
- module Paths
-
- #:stopdoc:
- require 'rbconfig'
-
- DOC_DIR = "doc/rdoc"
-
- version = Config::CONFIG['ruby_version']
-
- base = File.join(Config::CONFIG['datadir'], "ri", version)
- SYSDIR = File.join(base, "system")
- SITEDIR = File.join(base, "site")
- homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH']
-
- if homedir
- HOMEDIR = File.join(homedir, ".rdoc")
- else
- HOMEDIR = nil
- end
-
- # This is the search path for 'ri'
- PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)}
-
- begin
- require 'rubygems'
- GEMDIRS = Dir["#{Gem.path}/doc/*/ri"]
- GEMDIRS.each { |path| RI::Paths::PATH << path }
- rescue LoadError
- GEMDIRS = nil
- end
-
- # Returns the selected documentation directories as an Array, or PATH if no
- # overriding directories were given.
-
- def self.path(use_system, use_site, use_home, use_gems, *extra_dirs)
- path = raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
- return path.select { |path| File.directory? path }
- end
-
- # Returns the selected documentation directories including nonexistent
- # directories. Used to print out what paths were searched if no ri was
- # found.
-
- def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
- return PATH unless use_system or use_site or use_home or use_gems or
- not extra_dirs.empty?
-
- path = []
- path << extra_dirs unless extra_dirs.empty?
- path << RI::Paths::SYSDIR if use_system
- path << RI::Paths::SITEDIR if use_site
- path << RI::Paths::HOMEDIR if use_home
- path << RI::Paths::GEMDIRS if use_gems
-
- return path.flatten.compact
- end
-
- end
-end
diff --git a/lib/rdoc/ri/ri_reader.rb b/lib/rdoc/ri/ri_reader.rb
deleted file mode 100644
index fb2c373e38..0000000000
--- a/lib/rdoc/ri/ri_reader.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-require 'rdoc/ri/ri_descriptions'
-require 'rdoc/ri/ri_writer'
-require 'rdoc/markup/simple_markup/to_flow'
-
-module RI
- class RiReader
-
- def initialize(ri_cache)
- @cache = ri_cache
- end
-
- def top_level_namespace
- [ @cache.toplevel ]
- end
-
- def lookup_namespace_in(target, namespaces)
- result = []
- for n in namespaces
- result.concat(n.contained_modules_matching(target))
- end
- result
- end
-
- def find_class_by_name(full_name)
- names = full_name.split(/::/)
- ns = @cache.toplevel
- for name in names
- ns = ns.contained_class_named(name)
- return nil if ns.nil?
- end
- get_class(ns)
- end
-
- def find_methods(name, is_class_method, namespaces)
- result = []
- namespaces.each do |ns|
- result.concat ns.methods_matching(name, is_class_method)
- end
- result
- end
-
- # return the MethodDescription for a given MethodEntry
- # by deserializing the YAML
- def get_method(method_entry)
- path = method_entry.path_name
- File.open(path) { |f| RI::Description.deserialize(f) }
- end
-
- # Return a class description
- def get_class(class_entry)
- result = nil
- for path in class_entry.path_names
- path = RiWriter.class_desc_path(path, class_entry)
- desc = File.open(path) {|f| RI::Description.deserialize(f) }
- if result
- result.merge_in(desc)
- else
- result = desc
- end
- end
- result
- end
-
- # return the names of all classes and modules
- def full_class_names
- res = []
- find_classes_in(res, @cache.toplevel)
- end
-
- # return a list of all classes, modules, and methods
- def all_names
- res = []
- find_names_in(res, @cache.toplevel)
- end
-
- # ----
- private
- # ----
-
- def find_classes_in(res, klass)
- classes = klass.classes_and_modules
- for c in classes
- res << c.full_name
- find_classes_in(res, c)
- end
- res
- end
-
- def find_names_in(res, klass)
- classes = klass.classes_and_modules
- for c in classes
- res << c.full_name
- res.concat c.all_method_names
- find_names_in(res, c)
- end
- res
- end
-
- end
-end
diff --git a/lib/rdoc/ri/ri_util.rb b/lib/rdoc/ri/ri_util.rb
deleted file mode 100644
index 8a01255897..0000000000
--- a/lib/rdoc/ri/ri_util.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-######################################################################
-
-class RiError < Exception; end
-#
-# Break argument into its constituent class or module names, an
-# optional method type, and a method name
-
-class NameDescriptor
-
- attr_reader :class_names
- attr_reader :method_name
-
- # true and false have the obvious meaning. nil means we don't care
- attr_reader :is_class_method
-
- # arg may be
- # 1. a class or module name (optionally qualified with other class
- # or module names (Kernel, File::Stat etc)
- # 2. a method name
- # 3. a method name qualified by a optionally fully qualified class
- # or module name
- #
- # We're fairly casual about delimiters: folks can say Kernel::puts,
- # Kernel.puts, or Kernel\#puts for example. There's one exception:
- # if you say IO::read, we look for a class method, but if you
- # say IO.read, we look for an instance method
-
- def initialize(arg)
- @class_names = []
- separator = nil
-
- tokens = arg.split(/(\.|::|#)/)
-
- # Skip leading '::', '#' or '.', but remember it might
- # be a method name qualifier
- separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/
-
- # Skip leading '::', but remember we potentially have an inst
-
- # leading stuff must be class names
-
- while tokens[0] =~ /^[A-Z]/
- @class_names << tokens.shift
- unless tokens.empty?
- separator = tokens.shift
- break unless separator == "::"
- end
- end
-
- # Now must have a single token, the method name, or an empty
- # array
- unless tokens.empty?
- @method_name = tokens.shift
- # We may now have a trailing !, ?, or = to roll into
- # the method name
- if !tokens.empty? && tokens[0] =~ /^[!?=]$/
- @method_name << tokens.shift
- end
-
- if @method_name =~ /::|\.|#/ or !tokens.empty?
- raise RiError.new("Bad argument: #{arg}")
- end
- if separator && separator != '.'
- @is_class_method = separator == "::"
- end
- end
- end
-
- # Return the full class name (with '::' between the components)
- # or "" if there's no class name
-
- def full_class_name
- @class_names.join("::")
- end
-end
diff --git a/lib/rdoc/ri/ri_writer.rb b/lib/rdoc/ri/ri_writer.rb
deleted file mode 100644
index 78c68e8409..0000000000
--- a/lib/rdoc/ri/ri_writer.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'fileutils'
-
-module RI
- class RiWriter
-
- def RiWriter.class_desc_path(dir, class_desc)
- File.join(dir, "cdesc-" + class_desc.name + ".yaml")
- end
-
-
- # Convert a name from internal form (containing punctuation)
- # to an external form (where punctuation is replaced
- # by %xx)
-
- def RiWriter.internal_to_external(name)
- name.gsub(/\W/) { sprintf("%%%02x", $&[0]) }
- end
-
- # And the reverse operation
- def RiWriter.external_to_internal(name)
- name.gsub(/%([0-9a-f]{2,2})/) { $1.to_i(16).chr }
- end
-
- def initialize(base_dir)
- @base_dir = base_dir
- end
-
- def remove_class(class_desc)
- FileUtils.rm_rf(path_to_dir(class_desc.full_name))
- end
-
- def add_class(class_desc)
- dir = path_to_dir(class_desc.full_name)
- FileUtils.mkdir_p(dir)
- class_file_name = RiWriter.class_desc_path(dir, class_desc)
- File.open(class_file_name, "w") do |f|
- f.write(class_desc.serialize)
- end
- end
-
- def add_method(class_desc, method_desc)
- dir = path_to_dir(class_desc.full_name)
- file_name = RiWriter.internal_to_external(method_desc.name)
- meth_file_name = File.join(dir, file_name)
- if method_desc.is_singleton
- meth_file_name += "-c.yaml"
- else
- meth_file_name += "-i.yaml"
- end
-
- File.open(meth_file_name, "w") do |f|
- f.write(method_desc.serialize)
- end
- end
-
- private
-
- def path_to_dir(class_name)
- File.join(@base_dir, *class_name.split('::'))
- end
- end
-end
diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb
new file mode 100644
index 0000000000..9f4b03734a
--- /dev/null
+++ b/lib/rdoc/ri/store.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module RDoc::RI
+
+ Store = RDoc::Store # :nodoc:
+
+end
+
diff --git a/lib/rdoc/ri/task.rb b/lib/rdoc/ri/task.rb
new file mode 100644
index 0000000000..6a6ea572bf
--- /dev/null
+++ b/lib/rdoc/ri/task.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+begin
+ gem 'rdoc'
+rescue Gem::LoadError
+end unless defined?(RDoc)
+
+require 'rdoc/task'
+
+##
+# RDoc::RI::Task creates ri data in <code>./.rdoc</code> for your project.
+#
+# It contains the following tasks:
+#
+# [ri]
+# Build ri data
+#
+# [clobber_ri]
+# Delete ri data files. This target is automatically added to the main
+# clobber target.
+#
+# [reri]
+# Rebuild the ri data from scratch even if they are not out of date.
+#
+# Simple example:
+#
+# require 'rdoc/ri/task'
+#
+# RDoc::RI::Task.new do |ri|
+# ri.main = 'README.rdoc'
+# ri.rdoc_files.include 'README.rdoc', 'lib/**/*.rb'
+# end
+#
+# For further configuration details see RDoc::Task.
+
+class RDoc::RI::Task < RDoc::Task
+
+ DEFAULT_NAMES = { # :nodoc:
+ :clobber_rdoc => :clobber_ri,
+ :rdoc => :ri,
+ :rerdoc => :reri,
+ }
+
+ ##
+ # Create an ri task with the given name. See RDoc::Task for documentation on
+ # setting names.
+
+ def initialize name = DEFAULT_NAMES # :yield: self
+ super
+ end
+
+ def clobber_task_description # :nodoc:
+ "Remove RI data files"
+ end
+
+ ##
+ # Sets default task values
+
+ def defaults
+ super
+
+ @rdoc_dir = '.rdoc'
+ end
+
+ def rdoc_task_description # :nodoc:
+ 'Build RI data files'
+ end
+
+ def rerdoc_task_description # :nodoc:
+ 'Rebuild RI data files'
+ end
+end
diff --git a/lib/rdoc/rubygems_hook.rb b/lib/rdoc/rubygems_hook.rb
new file mode 100644
index 0000000000..90b0541fcf
--- /dev/null
+++ b/lib/rdoc/rubygems_hook.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+require 'rubygems/user_interaction'
+require 'fileutils'
+require 'rdoc'
+
+##
+# Gem::RDoc provides methods to generate RDoc and ri data for installed gems
+# upon gem installation.
+#
+# This file is automatically required by RubyGems 1.9 and newer.
+
+class RDoc::RubygemsHook
+
+ include Gem::UserInteraction
+ extend Gem::UserInteraction
+
+ @rdoc_version = nil
+ @specs = []
+
+ ##
+ # Force installation of documentation?
+
+ attr_accessor :force
+
+ ##
+ # Generate rdoc?
+
+ attr_accessor :generate_rdoc
+
+ ##
+ # Generate ri data?
+
+ attr_accessor :generate_ri
+
+ class << self
+
+ ##
+ # Loaded version of RDoc. Set by ::load_rdoc
+
+ attr_reader :rdoc_version
+
+ end
+
+ ##
+ # Post installs hook that generates documentation for each specification in
+ # +specs+
+
+ def self.generation_hook installer, specs
+ start = Time.now
+ types = installer.document
+
+ generate_rdoc = types.include? 'rdoc'
+ generate_ri = types.include? 'ri'
+
+ specs.each do |spec|
+ new(spec, generate_rdoc, generate_ri).generate
+ end
+
+ return unless generate_rdoc or generate_ri
+
+ duration = (Time.now - start).to_i
+ names = specs.map(&:name).join ', '
+
+ say "Done installing documentation for #{names} after #{duration} seconds"
+ end
+
+ ##
+ # Loads the RDoc generator
+
+ def self.load_rdoc
+ return if @rdoc_version
+
+ require 'rdoc/rdoc'
+
+ @rdoc_version = Gem::Version.new ::RDoc::VERSION
+ end
+
+ ##
+ # Creates a new documentation generator for +spec+. RDoc and ri data
+ # generation can be enabled or disabled through +generate_rdoc+ and
+ # +generate_ri+ respectively.
+ #
+ # Only +generate_ri+ is enabled by default.
+
+ def initialize spec, generate_rdoc = false, generate_ri = true
+ @doc_dir = spec.doc_dir
+ @force = false
+ @rdoc = nil
+ @spec = spec
+
+ @generate_rdoc = generate_rdoc
+ @generate_ri = generate_ri
+
+ @rdoc_dir = spec.doc_dir 'rdoc'
+ @ri_dir = spec.doc_dir 'ri'
+ end
+
+ ##
+ # Removes legacy rdoc arguments from +args+
+ #--
+ # TODO move to RDoc::Options
+
+ def delete_legacy_args args
+ args.delete '--inline-source'
+ args.delete '--promiscuous'
+ args.delete '-p'
+ args.delete '--one-file'
+ end
+
+ ##
+ # Generates documentation using the named +generator+ ("darkfish" or "ri")
+ # and following the given +options+.
+ #
+ # Documentation will be generated into +destination+
+
+ def document generator, options, destination
+ generator_name = generator
+
+ options = options.dup
+ options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
+ options.setup_generator generator
+ options.op_dir = destination
+ options.finish
+
+ generator = options.generator.new @rdoc.store, options
+
+ @rdoc.options = options
+ @rdoc.generator = generator
+
+ say "Installing #{generator_name} documentation for #{@spec.full_name}"
+
+ FileUtils.mkdir_p options.op_dir
+
+ Dir.chdir options.op_dir do
+ begin
+ @rdoc.class.current = @rdoc
+ @rdoc.generator.generate
+ ensure
+ @rdoc.class.current = nil
+ end
+ end
+ end
+
+ ##
+ # Generates RDoc and ri data
+
+ def generate
+ return if @spec.default_gem?
+ return unless @generate_ri or @generate_rdoc
+
+ setup
+
+ options = nil
+
+ args = @spec.rdoc_options
+ args.concat @spec.source_paths
+ args.concat @spec.extra_rdoc_files
+
+ case config_args = Gem.configuration[:rdoc]
+ when String then
+ args = args.concat config_args.split
+ when Array then
+ args = args.concat config_args
+ end
+
+ delete_legacy_args args
+
+ Dir.chdir @spec.full_gem_path do
+ options = ::RDoc::Options.new
+ options.default_title = "#{@spec.full_name} Documentation"
+ options.parse args
+ end
+
+ options.quiet = !Gem.configuration.really_verbose
+
+ @rdoc = new_rdoc
+ @rdoc.options = options
+
+ store = RDoc::Store.new
+ store.encoding = options.encoding
+ store.dry_run = options.dry_run
+ store.main = options.main_page
+ store.title = options.title
+
+ @rdoc.store = store
+
+ say "Parsing documentation for #{@spec.full_name}"
+
+ Dir.chdir @spec.full_gem_path do
+ @rdoc.parse_files options.files
+ end
+
+ document 'ri', options, @ri_dir if
+ @generate_ri and (@force or not File.exist? @ri_dir)
+
+ document 'darkfish', options, @rdoc_dir if
+ @generate_rdoc and (@force or not File.exist? @rdoc_dir)
+ end
+
+ ##
+ # #new_rdoc creates a new RDoc instance. This method is provided only to
+ # make testing easier.
+
+ def new_rdoc # :nodoc:
+ ::RDoc::RDoc.new
+ end
+
+ ##
+ # Is rdoc documentation installed?
+
+ def rdoc_installed?
+ File.exist? @rdoc_dir
+ end
+
+ ##
+ # Removes generated RDoc and ri data
+
+ def remove
+ base_dir = @spec.base_dir
+
+ raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
+
+ FileUtils.rm_rf @rdoc_dir
+ FileUtils.rm_rf @ri_dir
+ end
+
+ ##
+ # Is ri data installed?
+
+ def ri_installed?
+ File.exist? @ri_dir
+ end
+
+ ##
+ # Prepares the spec for documentation generation
+
+ def setup
+ self.class.load_rdoc
+
+ raise Gem::FilePermissionError, @doc_dir if
+ File.exist?(@doc_dir) and not File.writable?(@doc_dir)
+
+ FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
+ end
+
+end
diff --git a/lib/rdoc/servlet.rb b/lib/rdoc/servlet.rb
new file mode 100644
index 0000000000..f2d6dd5adc
--- /dev/null
+++ b/lib/rdoc/servlet.rb
@@ -0,0 +1,442 @@
+# frozen_string_literal: true
+require 'rdoc'
+require 'time'
+require 'json'
+require 'webrick'
+
+##
+# This is a WEBrick servlet that allows you to browse ri documentation.
+#
+# You can show documentation through either `ri --server` or, with RubyGems
+# 2.0 or newer, `gem server`. For ri, the server runs on port 8214 by
+# default. For RubyGems the server runs on port 8808 by default.
+#
+# You can use this servlet in your own project by mounting it on a WEBrick
+# server:
+#
+# require 'webrick'
+#
+# server = WEBrick::HTTPServer.new Port: 8000
+#
+# server.mount '/', RDoc::Servlet
+#
+# If you want to mount the servlet some other place than the root, provide the
+# base path when mounting:
+#
+# server.mount '/rdoc', RDoc::Servlet, '/rdoc'
+
+class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
+
+ @server_stores = Hash.new { |hash, server| hash[server] = {} }
+ @cache = Hash.new { |hash, store| hash[store] = {} }
+
+ ##
+ # Maps an asset type to its path on the filesystem
+
+ attr_reader :asset_dirs
+
+ ##
+ # An RDoc::Options instance used for rendering options
+
+ attr_reader :options
+
+ ##
+ # Creates an instance of this servlet that shares cached data between
+ # requests.
+
+ def self.get_instance server, *options # :nodoc:
+ stores = @server_stores[server]
+
+ new server, stores, @cache, *options
+ end
+
+ ##
+ # Creates a new WEBrick servlet.
+ #
+ # Use +mount_path+ when mounting the servlet somewhere other than /.
+ #
+ # Use +extra_doc_dirs+ for additional documentation directories.
+ #
+ # +server+ is provided automatically by WEBrick when mounting. +stores+ and
+ # +cache+ are provided automatically by the servlet.
+
+ def initialize server, stores, cache, mount_path = nil, extra_doc_dirs = []
+ super server
+
+ @cache = cache
+ @mount_path = mount_path
+ @extra_doc_dirs = extra_doc_dirs
+ @stores = stores
+
+ @options = RDoc::Options.new
+ @options.op_dir = '.'
+
+ darkfish_dir = nil
+
+ # HACK dup
+ $LOAD_PATH.each do |path|
+ darkfish_dir = File.join path, 'rdoc/generator/template/darkfish/'
+ next unless File.directory? darkfish_dir
+ @options.template_dir = darkfish_dir
+ break
+ end
+
+ @asset_dirs = {
+ :darkfish => darkfish_dir,
+ :json_index =>
+ File.expand_path('../generator/template/json_index/', __FILE__),
+ }
+ end
+
+ ##
+ # Serves the asset at the path in +req+ for +generator_name+ via +res+.
+
+ def asset generator_name, req, res
+ asset_dir = @asset_dirs[generator_name]
+
+ asset_path = File.join asset_dir, req.path
+
+ if_modified_since req, res, asset_path
+
+ res.body = File.read asset_path
+
+ res.content_type = case req.path
+ when /css$/ then 'text/css'
+ when /js$/ then 'application/javascript'
+ else 'application/octet-stream'
+ end
+ end
+
+ ##
+ # GET request entry point. Fills in +res+ for the path, etc. in +req+.
+
+ def do_GET req, res
+ req.path = req.path.sub(/^#{Regexp.escape @mount_path}/o, '') if @mount_path
+
+ case req.path
+ when '/' then
+ root req, res
+ when '/js/darkfish.js', '/js/jquery.js', '/js/search.js',
+ %r%^/css/%, %r%^/images/%, %r%^/fonts/% then
+ asset :darkfish, req, res
+ when '/js/navigation.js', '/js/searcher.js' then
+ asset :json_index, req, res
+ when '/js/search_index.js' then
+ root_search req, res
+ else
+ show_documentation req, res
+ end
+ rescue WEBrick::HTTPStatus::NotFound => e
+ generator = generator_for RDoc::Store.new
+
+ not_found generator, req, res, e.message
+ rescue WEBrick::HTTPStatus::Status
+ raise
+ rescue => e
+ error e, req, res
+ end
+
+ ##
+ # Fills in +res+ with the class, module or page for +req+ from +store+.
+ #
+ # +path+ is relative to the mount_path and is used to determine the class,
+ # module or page name (/RDoc/Servlet.html becomes RDoc::Servlet).
+ # +generator+ is used to create the page.
+
+ def documentation_page store, generator, path, req, res
+ name = path.sub(/.html$/, '').gsub '/', '::'
+
+ if klass = store.find_class_or_module(name) then
+ res.body = generator.generate_class klass
+ elsif page = store.find_text_page(name.sub(/_([^_]*)$/, '.\1')) then
+ res.body = generator.generate_page page
+ else
+ not_found generator, req, res
+ end
+ end
+
+ ##
+ # Creates the JSON search index on +res+ for the given +store+. +generator+
+ # must respond to \#json_index to build. +req+ is ignored.
+
+ def documentation_search store, generator, req, res
+ json_index = @cache[store].fetch :json_index do
+ @cache[store][:json_index] =
+ JSON.dump generator.json_index.build_index
+ end
+
+ res.content_type = 'application/javascript'
+ res.body = "var search_data = #{json_index}"
+ end
+
+ ##
+ # Returns the RDoc::Store and path relative to +mount_path+ for
+ # documentation at +path+.
+
+ def documentation_source path
+ _, source_name, path = path.split '/', 3
+
+ store = @stores[source_name]
+ return store, path if store
+
+ store = store_for source_name
+
+ store.load_all
+
+ @stores[source_name] = store
+
+ return store, path
+ end
+
+ ##
+ # Generates an error page for the +exception+ while handling +req+ on +res+.
+
+ def error exception, req, res
+ backtrace = exception.backtrace.join "\n"
+
+ res.content_type = 'text/html'
+ res.status = 500
+ res.body = <<-BODY
+<!DOCTYPE html>
+<html>
+<head>
+<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+
+<title>Error - #{ERB::Util.html_escape exception.class}</title>
+
+<link type="text/css" media="screen" href="#{@mount_path}/css/rdoc.css" rel="stylesheet">
+</head>
+<body>
+<h1>Error</h1>
+
+<p>While processing <code>#{ERB::Util.html_escape req.request_uri}</code> the
+RDoc (#{ERB::Util.html_escape RDoc::VERSION}) server has encountered a
+<code>#{ERB::Util.html_escape exception.class}</code>
+exception:
+
+<pre>#{ERB::Util.html_escape exception.message}</pre>
+
+<p>Please report this to the
+<a href="https://github.com/ruby/rdoc/issues">RDoc issues tracker</a>. Please
+include the RDoc version, the URI above and exception class, message and
+backtrace. If you're viewing a gem's documentation, include the gem name and
+version. If you're viewing Ruby's documentation, include the version of ruby.
+
+<p>Backtrace:
+
+<pre>#{ERB::Util.html_escape backtrace}</pre>
+
+</body>
+</html>
+ BODY
+ end
+
+ ##
+ # Instantiates a Darkfish generator for +store+
+
+ def generator_for store
+ generator = RDoc::Generator::Darkfish.new store, @options
+ generator.file_output = false
+ generator.asset_rel_path = '..'
+
+ rdoc = RDoc::RDoc.new
+ rdoc.store = store
+ rdoc.generator = generator
+ rdoc.options = @options
+
+ @options.main_page = store.main
+ @options.title = store.title
+
+ generator
+ end
+
+ ##
+ # Handles the If-Modified-Since HTTP header on +req+ for +path+. If the
+ # file has not been modified a Not Modified response is returned. If the
+ # file has been modified a Last-Modified header is added to +res+.
+
+ def if_modified_since req, res, path = nil
+ last_modified = File.stat(path).mtime if path
+
+ res['last-modified'] = last_modified.httpdate
+
+ return unless ims = req['if-modified-since']
+
+ ims = Time.parse ims
+
+ unless ims < last_modified then
+ res.body = ''
+ raise WEBrick::HTTPStatus::NotModified
+ end
+ end
+
+ ##
+ # Returns an Array of installed documentation.
+ #
+ # Each entry contains the documentation name (gem name, 'Ruby
+ # Documentation', etc.), the path relative to the mount point, whether the
+ # documentation exists, the type of documentation (See RDoc::RI::Paths#each)
+ # and the filesystem to the RDoc::Store for the documentation.
+
+ def installed_docs
+ extra_counter = 0
+ ri_paths.map do |path, type|
+ store = RDoc::Store.new path, type
+ exists = File.exist? store.cache_path
+
+ case type
+ when :gem then
+ gem_path = path[%r%/([^/]*)/ri$%, 1]
+ [gem_path, "#{gem_path}/", exists, type, path]
+ when :system then
+ ['Ruby Documentation', 'ruby/', exists, type, path]
+ when :site then
+ ['Site Documentation', 'site/', exists, type, path]
+ when :home then
+ ['Home Documentation', 'home/', exists, type, path]
+ when :extra then
+ extra_counter += 1
+ store.load_cache if exists
+ title = store.title || "Extra Documentation"
+ [title, "extra-#{extra_counter}/", exists, type, path]
+ end
+ end
+ end
+
+ ##
+ # Returns a 404 page built by +generator+ for +req+ on +res+.
+
+ def not_found generator, req, res, message = nil
+ message ||= "The page <kbd>#{ERB::Util.h req.path}</kbd> was not found"
+ res.body = generator.generate_servlet_not_found message
+ res.status = 404
+ end
+
+ ##
+ # Enumerates the ri paths. See RDoc::RI::Paths#each
+
+ def ri_paths &block
+ RDoc::RI::Paths.each true, true, true, :all, *@extra_doc_dirs, &block #TODO: pass extra_dirs
+ end
+
+ ##
+ # Generates the root page on +res+. +req+ is ignored.
+
+ def root req, res
+ generator = RDoc::Generator::Darkfish.new nil, @options
+
+ res.body = generator.generate_servlet_root installed_docs
+
+ res.content_type = 'text/html'
+ end
+
+ ##
+ # Generates a search index for the root page on +res+. +req+ is ignored.
+
+ def root_search req, res
+ search_index = []
+ info = []
+
+ installed_docs.map do |name, href, exists, type, path|
+ next unless exists
+
+ search_index << name
+
+ case type
+ when :gem
+ gemspec = path.gsub(%r%/doc/([^/]*?)/ri$%,
+ '/specifications/\1.gemspec')
+
+ spec = Gem::Specification.load gemspec
+
+ path = spec.full_name
+ comment = spec.summary
+ when :system then
+ path = 'ruby'
+ comment = 'Documentation for the Ruby standard library'
+ when :site then
+ path = 'site'
+ comment = 'Documentation for non-gem libraries'
+ when :home then
+ path = 'home'
+ comment = 'Documentation from your home directory'
+ when :extra
+ comment = name
+ end
+
+ info << [name, '', path, '', comment]
+ end
+
+ index = {
+ :index => {
+ :searchIndex => search_index,
+ :longSearchIndex => search_index,
+ :info => info,
+ }
+ }
+
+ res.body = "var search_data = #{JSON.dump index};"
+ res.content_type = 'application/javascript'
+ end
+
+ ##
+ # Displays documentation for +req+ on +res+, whether that be HTML or some
+ # asset.
+
+ def show_documentation req, res
+ store, path = documentation_source req.path
+
+ if_modified_since req, res, store.cache_path
+
+ generator = generator_for store
+
+ case path
+ when nil, '', 'index.html' then
+ res.body = generator.generate_index
+ when 'table_of_contents.html' then
+ res.body = generator.generate_table_of_contents
+ when 'js/search_index.js' then
+ documentation_search store, generator, req, res
+ else
+ documentation_page store, generator, path, req, res
+ end
+ ensure
+ res.content_type ||= 'text/html'
+ end
+
+ ##
+ # Returns an RDoc::Store for the given +source_name+ ('ruby' or a gem name).
+
+ def store_for source_name
+ case source_name
+ when 'home' then
+ RDoc::Store.new RDoc::RI::Paths.home_dir, :home
+ when 'ruby' then
+ RDoc::Store.new RDoc::RI::Paths.system_dir, :system
+ when 'site' then
+ RDoc::Store.new RDoc::RI::Paths.site_dir, :site
+ when /^extra-(\d+)$/ then
+ index = $1.to_i - 1
+ ri_dir = installed_docs[index][4]
+ RDoc::Store.new ri_dir, :extra
+ else
+ ri_dir, type = ri_paths.find do |dir, dir_type|
+ next unless dir_type == :gem
+
+ source_name == dir[%r%/([^/]*)/ri$%, 1]
+ end
+
+ raise WEBrick::HTTPStatus::NotFound,
+ "Could not find gem \"#{source_name}\". Are you sure you installed it?" unless ri_dir
+
+ store = RDoc::Store.new ri_dir, type
+
+ return store if File.exist? store.cache_path
+
+ raise WEBrick::HTTPStatus::NotFound,
+ "Could not find documentation for \"#{source_name}\". Please run `gem rdoc --ri gem_name`"
+
+ end
+ end
+
+end
diff --git a/lib/rdoc/single_class.rb b/lib/rdoc/single_class.rb
new file mode 100644
index 0000000000..6a7b67deb3
--- /dev/null
+++ b/lib/rdoc/single_class.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+##
+# A singleton class
+
+class RDoc::SingleClass < RDoc::ClassModule
+
+ ##
+ # Adds the superclass to the included modules.
+
+ def ancestors
+ superclass ? super + [superclass] : super
+ end
+
+ def aref_prefix # :nodoc:
+ 'sclass'
+ end
+
+ ##
+ # The definition of this singleton class, <tt>class << MyClassName</tt>
+
+ def definition
+ "class << #{full_name}"
+ end
+
+end
+
diff --git a/lib/rdoc/stats.rb b/lib/rdoc/stats.rb
new file mode 100644
index 0000000000..bd6c0ef23a
--- /dev/null
+++ b/lib/rdoc/stats.rb
@@ -0,0 +1,462 @@
+# frozen_string_literal: true
+##
+# RDoc statistics collector which prints a summary and report of a project's
+# documentation totals.
+
+class RDoc::Stats
+
+ include RDoc::Text
+
+ ##
+ # Output level for the coverage report
+
+ attr_reader :coverage_level
+
+ ##
+ # Count of files parsed during parsing
+
+ attr_reader :files_so_far
+
+ ##
+ # Total number of files found
+
+ attr_reader :num_files
+
+ ##
+ # Creates a new Stats that will have +num_files+. +verbosity+ defaults to 1
+ # which will create an RDoc::Stats::Normal outputter.
+
+ def initialize store, num_files, verbosity = 1
+ @num_files = num_files
+ @store = store
+
+ @coverage_level = 0
+ @doc_items = nil
+ @files_so_far = 0
+ @fully_documented = false
+ @num_params = 0
+ @percent_doc = nil
+ @start = Time.now
+ @undoc_params = 0
+
+ @display = case verbosity
+ when 0 then Quiet.new num_files
+ when 1 then Normal.new num_files
+ else Verbose.new num_files
+ end
+ end
+
+ ##
+ # Records the parsing of an alias +as+.
+
+ def add_alias as
+ @display.print_alias as
+ end
+
+ ##
+ # Records the parsing of an attribute +attribute+
+
+ def add_attribute attribute
+ @display.print_attribute attribute
+ end
+
+ ##
+ # Records the parsing of a class +klass+
+
+ def add_class klass
+ @display.print_class klass
+ end
+
+ ##
+ # Records the parsing of +constant+
+
+ def add_constant constant
+ @display.print_constant constant
+ end
+
+ ##
+ # Records the parsing of +file+
+
+ def add_file(file)
+ @files_so_far += 1
+ @display.print_file @files_so_far, file
+ end
+
+ ##
+ # Records the parsing of +method+
+
+ def add_method(method)
+ @display.print_method method
+ end
+
+ ##
+ # Records the parsing of a module +mod+
+
+ def add_module(mod)
+ @display.print_module mod
+ end
+
+ ##
+ # Call this to mark the beginning of parsing for display purposes
+
+ def begin_adding
+ @display.begin_adding
+ end
+
+ ##
+ # Calculates documentation totals and percentages for classes, modules,
+ # constants, attributes and methods.
+
+ def calculate
+ return if @doc_items
+
+ ucm = @store.unique_classes_and_modules
+
+ classes = @store.unique_classes.reject { |cm| cm.full_name == 'Object' }
+
+ constants = []
+ ucm.each { |cm| constants.concat cm.constants }
+
+ methods = []
+ ucm.each { |cm| methods.concat cm.method_list }
+
+ attributes = []
+ ucm.each { |cm| attributes.concat cm.attributes }
+
+ @num_attributes, @undoc_attributes = doc_stats attributes
+ @num_classes, @undoc_classes = doc_stats classes
+ @num_constants, @undoc_constants = doc_stats constants
+ @num_methods, @undoc_methods = doc_stats methods
+ @num_modules, @undoc_modules = doc_stats @store.unique_modules
+
+ @num_items =
+ @num_attributes +
+ @num_classes +
+ @num_constants +
+ @num_methods +
+ @num_modules +
+ @num_params
+
+ @undoc_items =
+ @undoc_attributes +
+ @undoc_classes +
+ @undoc_constants +
+ @undoc_methods +
+ @undoc_modules +
+ @undoc_params
+
+ @doc_items = @num_items - @undoc_items
+ end
+
+ ##
+ # Sets coverage report level. Accepted values are:
+ #
+ # false or nil:: No report
+ # 0:: Classes, modules, constants, attributes, methods
+ # 1:: Level 0 + method parameters
+
+ def coverage_level= level
+ level = -1 unless level
+
+ @coverage_level = level
+ end
+
+ ##
+ # Returns the length and number of undocumented items in +collection+.
+
+ def doc_stats collection
+ visible = collection.select { |item| item.display? }
+ [visible.length, visible.count { |item| not item.documented? }]
+ end
+
+ ##
+ # Call this to mark the end of parsing for display purposes
+
+ def done_adding
+ @display.done_adding
+ end
+
+ ##
+ # The documentation status of this project. +true+ when 100%, +false+ when
+ # less than 100% and +nil+ when unknown.
+ #
+ # Set by calling #calculate
+
+ def fully_documented?
+ @fully_documented
+ end
+
+ ##
+ # A report that says you did a great job!
+
+ def great_job
+ report = RDoc::Markup::Document.new
+
+ report << RDoc::Markup::Paragraph.new('100% documentation!')
+ report << RDoc::Markup::Paragraph.new('Great Job!')
+
+ report
+ end
+
+ ##
+ # Calculates the percentage of items documented.
+
+ def percent_doc
+ return @percent_doc if @percent_doc
+
+ @fully_documented = (@num_items - @doc_items) == 0
+
+ @percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero?
+ @percent_doc ||= 0
+
+ @percent_doc
+ end
+
+ ##
+ # Returns a report on which items are not documented
+
+ def report
+ if @coverage_level > 0 then
+ extend RDoc::Text
+ end
+
+ if @coverage_level.zero? then
+ calculate
+
+ return great_job if @num_items == @doc_items
+ end
+
+ ucm = @store.unique_classes_and_modules
+
+ report = RDoc::Markup::Document.new
+ report << RDoc::Markup::Paragraph.new('The following items are not documented:')
+ report << RDoc::Markup::BlankLine.new
+
+ ucm.sort.each do |cm|
+ body = report_class_module(cm) {
+ [
+ report_constants(cm),
+ report_attributes(cm),
+ report_methods(cm),
+ ].compact
+ }
+
+ report << body if body
+ end
+
+ if @coverage_level > 0 then
+ calculate
+
+ return great_job if @num_items == @doc_items
+ end
+
+ report
+ end
+
+ ##
+ # Returns a report on undocumented attributes in ClassModule +cm+
+
+ def report_attributes cm
+ return if cm.attributes.empty?
+
+ report = []
+
+ cm.each_attribute do |attr|
+ next if attr.documented?
+ line = attr.line ? ":#{attr.line}" : nil
+ report << " #{attr.definition} :#{attr.name} # in file #{attr.file.full_name}#{line}\n"
+ report << "\n"
+ end
+
+ report
+ end
+
+ ##
+ # Returns a report on undocumented items in ClassModule +cm+
+
+ def report_class_module cm
+ return if cm.fully_documented? and @coverage_level.zero?
+ return unless cm.display?
+
+ report = RDoc::Markup::Document.new
+
+ if cm.in_files.empty? then
+ report << RDoc::Markup::Paragraph.new("#{cm.definition} is referenced but empty.")
+ report << RDoc::Markup::Paragraph.new("It probably came from another project. I'm sorry I'm holding it against you.")
+
+ return report
+ elsif cm.documented? then
+ documented = true
+ klass = RDoc::Markup::Verbatim.new("#{cm.definition} # is documented\n")
+ else
+ report << RDoc::Markup::Paragraph.new('In files:')
+
+ list = RDoc::Markup::List.new :BULLET
+
+ cm.in_files.each do |file|
+ para = RDoc::Markup::Paragraph.new file.full_name
+ list << RDoc::Markup::ListItem.new(nil, para)
+ end
+
+ report << list
+ report << RDoc::Markup::BlankLine.new
+
+ klass = RDoc::Markup::Verbatim.new("#{cm.definition}\n")
+ end
+
+ klass << "\n"
+
+ body = yield.flatten # HACK remove #flatten
+
+ if body.empty? then
+ return if documented
+
+ klass.parts.pop
+ else
+ klass.parts.concat body
+ end
+
+ klass << "end\n"
+
+ report << klass
+
+ report
+ end
+
+ ##
+ # Returns a report on undocumented constants in ClassModule +cm+
+
+ def report_constants cm
+ return if cm.constants.empty?
+
+ report = []
+
+ cm.each_constant do |constant|
+ # TODO constant aliases are listed in the summary but not reported
+ # figure out what to do here
+ next if constant.documented? || constant.is_alias_for
+
+ line = constant.line ? ":#{constant.line}" : line
+ report << " # in file #{constant.file.full_name}#{line}\n"
+ report << " #{constant.name} = nil\n"
+ report << "\n"
+ end
+
+ report
+ end
+
+ ##
+ # Returns a report on undocumented methods in ClassModule +cm+
+
+ def report_methods cm
+ return if cm.method_list.empty?
+
+ report = []
+
+ cm.each_method do |method|
+ next if method.documented? and @coverage_level.zero?
+
+ if @coverage_level > 0 then
+ params, undoc = undoc_params method
+
+ @num_params += params
+
+ unless undoc.empty? then
+ @undoc_params += undoc.length
+
+ undoc = undoc.map do |param| "+#{param}+" end
+ param_report = " # #{undoc.join ', '} is not documented\n"
+ end
+ end
+
+ next if method.documented? and not param_report
+
+ line = method.line ? ":#{method.line}" : nil
+ scope = method.singleton ? 'self.' : nil
+
+ report << " # in file #{method.file.full_name}#{line}\n"
+ report << param_report if param_report
+ report << " def #{scope}#{method.name}#{method.params}; end\n"
+ report << "\n"
+ end
+
+ report
+ end
+
+ ##
+ # Returns a summary of the collected statistics.
+
+ def summary
+ calculate
+
+ num_width = [@num_files, @num_items].max.to_s.length
+ undoc_width = [
+ @undoc_attributes,
+ @undoc_classes,
+ @undoc_constants,
+ @undoc_items,
+ @undoc_methods,
+ @undoc_modules,
+ @undoc_params,
+ ].max.to_s.length
+
+ report = RDoc::Markup::Verbatim.new
+
+ report << "Files: %*d\n" % [num_width, @num_files]
+
+ report << "\n"
+
+ report << "Classes: %*d (%*d undocumented)\n" % [
+ num_width, @num_classes, undoc_width, @undoc_classes]
+ report << "Modules: %*d (%*d undocumented)\n" % [
+ num_width, @num_modules, undoc_width, @undoc_modules]
+ report << "Constants: %*d (%*d undocumented)\n" % [
+ num_width, @num_constants, undoc_width, @undoc_constants]
+ report << "Attributes: %*d (%*d undocumented)\n" % [
+ num_width, @num_attributes, undoc_width, @undoc_attributes]
+ report << "Methods: %*d (%*d undocumented)\n" % [
+ num_width, @num_methods, undoc_width, @undoc_methods]
+ report << "Parameters: %*d (%*d undocumented)\n" % [
+ num_width, @num_params, undoc_width, @undoc_params] if
+ @coverage_level > 0
+
+ report << "\n"
+
+ report << "Total: %*d (%*d undocumented)\n" % [
+ num_width, @num_items, undoc_width, @undoc_items]
+
+ report << "%6.2f%% documented\n" % percent_doc
+ report << "\n"
+ report << "Elapsed: %0.1fs\n" % (Time.now - @start)
+
+ RDoc::Markup::Document.new report
+ end
+
+ ##
+ # Determines which parameters in +method+ were not documented. Returns a
+ # total parameter count and an Array of undocumented methods.
+
+ def undoc_params method
+ @formatter ||= RDoc::Markup::ToTtOnly.new
+
+ params = method.param_list
+
+ params = params.map { |param| param.gsub(/^\*\*?/, '') }
+
+ return 0, [] if params.empty?
+
+ document = parse method.comment
+
+ tts = document.accept @formatter
+
+ undoc = params - tts
+
+ [params.length, undoc]
+ end
+
+ autoload :Quiet, 'rdoc/stats/quiet'
+ autoload :Normal, 'rdoc/stats/normal'
+ autoload :Verbose, 'rdoc/stats/verbose'
+
+end
+
diff --git a/lib/rdoc/stats/normal.rb b/lib/rdoc/stats/normal.rb
new file mode 100644
index 0000000000..a3a6ff377e
--- /dev/null
+++ b/lib/rdoc/stats/normal.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+begin
+ require 'io/console/size'
+rescue LoadError
+ # for JRuby
+ require 'io/console'
+end
+
+##
+# Stats printer that prints just the files being documented with a progress
+# bar
+
+class RDoc::Stats::Normal < RDoc::Stats::Quiet
+
+ def begin_adding # :nodoc:
+ puts "Parsing sources..."
+ @last_width = 0
+ end
+
+ ##
+ # Prints a file with a progress bar
+
+ def print_file files_so_far, filename
+ progress_bar = sprintf("%3d%% [%2d/%2d] ",
+ 100 * files_so_far / @num_files,
+ files_so_far,
+ @num_files)
+
+ # Print a progress bar, but make sure it fits on a single line. Filename
+ # will be truncated if necessary.
+ size = IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize
+ terminal_width = size[1].to_i.nonzero? || 80
+ max_filename_size = terminal_width - progress_bar.size
+
+ if filename.size > max_filename_size then
+ # Turn "some_long_filename.rb" to "...ong_filename.rb"
+ filename = filename[(filename.size - max_filename_size) .. -1]
+ filename[0..2] = "..."
+ end
+
+ line = "#{progress_bar}#{filename}"
+ if $stdout.tty?
+ # Clean the line with whitespaces so that leftover output from the
+ # previous line doesn't show up.
+ $stdout.print("\r" + (" " * @last_width) + ("\b" * @last_width) + "\r") if @last_width && @last_width > 0
+ @last_width = line.size
+ $stdout.print("#{line}\r")
+ else
+ $stdout.puts(line)
+ end
+ $stdout.flush
+ end
+
+ def done_adding # :nodoc:
+ puts
+ end
+
+end
diff --git a/lib/rdoc/stats/quiet.rb b/lib/rdoc/stats/quiet.rb
new file mode 100644
index 0000000000..bc4161b2d4
--- /dev/null
+++ b/lib/rdoc/stats/quiet.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+##
+# Stats printer that prints nothing
+
+class RDoc::Stats::Quiet
+
+ ##
+ # Creates a new Quiet that will print nothing
+
+ def initialize num_files
+ @num_files = num_files
+ end
+
+ ##
+ # Prints a message at the beginning of parsing
+
+ def begin_adding(*) end
+
+ ##
+ # Prints when an alias is added
+
+ def print_alias(*) end
+
+ ##
+ # Prints when an attribute is added
+
+ def print_attribute(*) end
+
+ ##
+ # Prints when a class is added
+
+ def print_class(*) end
+
+ ##
+ # Prints when a constant is added
+
+ def print_constant(*) end
+
+ ##
+ # Prints when a file is added
+
+ def print_file(*) end
+
+ ##
+ # Prints when a method is added
+
+ def print_method(*) end
+
+ ##
+ # Prints when a module is added
+
+ def print_module(*) end
+
+ ##
+ # Prints when RDoc is done
+
+ def done_adding(*) end
+
+end
+
diff --git a/lib/rdoc/stats/verbose.rb b/lib/rdoc/stats/verbose.rb
new file mode 100644
index 0000000000..6ace8937a2
--- /dev/null
+++ b/lib/rdoc/stats/verbose.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+##
+# Stats printer that prints everything documented, including the documented
+# status
+
+class RDoc::Stats::Verbose < RDoc::Stats::Normal
+
+ ##
+ # Returns a marker for RDoc::CodeObject +co+ being undocumented
+
+ def nodoc co
+ " (undocumented)" unless co.documented?
+ end
+
+ def print_alias as # :nodoc:
+ puts " alias #{as.new_name} #{as.old_name}#{nodoc as}"
+ end
+
+ def print_attribute attribute # :nodoc:
+ puts " #{attribute.definition} #{attribute.name}#{nodoc attribute}"
+ end
+
+ def print_class(klass) # :nodoc:
+ puts " class #{klass.full_name}#{nodoc klass}"
+ end
+
+ def print_constant(constant) # :nodoc:
+ puts " #{constant.name}#{nodoc constant}"
+ end
+
+ def print_file(files_so_far, file) # :nodoc:
+ super
+ puts
+ end
+
+ def print_method(method) # :nodoc:
+ puts " #{method.singleton ? '::' : '#'}#{method.name}#{nodoc method}"
+ end
+
+ def print_module(mod) # :nodoc:
+ puts " module #{mod.full_name}#{nodoc mod}"
+ end
+
+end
+
+
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
new file mode 100644
index 0000000000..999aa76f92
--- /dev/null
+++ b/lib/rdoc/store.rb
@@ -0,0 +1,968 @@
+# frozen_string_literal: true
+require 'fileutils'
+
+##
+# A set of rdoc data for a single project (gem, path, etc.).
+#
+# The store manages reading and writing ri data for a project and maintains a
+# cache of methods, classes and ancestors in the store.
+#
+# The store maintains a #cache of its contents for faster lookup. After
+# adding items to the store it must be flushed using #save_cache. The cache
+# contains the following structures:
+#
+# @cache = {
+# :ancestors => {}, # class name => ancestor names
+# :attributes => {}, # class name => attributes
+# :class_methods => {}, # class name => class methods
+# :instance_methods => {}, # class name => instance methods
+# :modules => [], # classes and modules in this store
+# :pages => [], # page names
+# }
+#--
+# TODO need to prune classes
+
+class RDoc::Store
+
+ ##
+ # Errors raised from loading or saving the store
+
+ class Error < RDoc::Error
+ end
+
+ ##
+ # Raised when a stored file for a class, module, page or method is missing.
+
+ class MissingFileError < Error
+
+ ##
+ # The store the file should exist in
+
+ attr_reader :store
+
+ ##
+ # The file the #name should be saved as
+
+ attr_reader :file
+
+ ##
+ # The name of the object the #file would be loaded from
+
+ attr_reader :name
+
+ ##
+ # Creates a new MissingFileError for the missing +file+ for the given
+ # +name+ that should have been in the +store+.
+
+ def initialize store, file, name
+ @store = store
+ @file = file
+ @name = name
+ end
+
+ def message # :nodoc:
+ "store at #{@store.path} missing file #{@file} for #{@name}"
+ end
+
+ end
+
+ ##
+ # Stores the name of the C variable a class belongs to. This helps wire up
+ # classes defined from C across files.
+
+ attr_reader :c_enclosure_classes # :nodoc:
+
+ attr_reader :c_enclosure_names # :nodoc:
+
+ ##
+ # Maps C variables to class or module names for each parsed C file.
+
+ attr_reader :c_class_variables
+
+ ##
+ # Maps C variables to singleton class names for each parsed C file.
+
+ attr_reader :c_singleton_class_variables
+
+ ##
+ # If true this Store will not write any files
+
+ attr_accessor :dry_run
+
+ ##
+ # Path this store reads or writes
+
+ attr_accessor :path
+
+ ##
+ # The RDoc::RDoc driver for this parse tree. This allows classes consulting
+ # the documentation tree to access user-set options, for example.
+
+ attr_accessor :rdoc
+
+ ##
+ # Type of ri datastore this was loaded from. See RDoc::RI::Driver,
+ # RDoc::RI::Paths.
+
+ attr_accessor :type
+
+ ##
+ # The contents of the Store
+
+ attr_reader :cache
+
+ ##
+ # The encoding of the contents in the Store
+
+ attr_accessor :encoding
+
+ ##
+ # Creates a new Store of +type+ that will load or save to +path+
+
+ def initialize path = nil, type = nil
+ @dry_run = false
+ @encoding = nil
+ @path = path
+ @rdoc = nil
+ @type = type
+
+ @cache = {
+ :ancestors => {},
+ :attributes => {},
+ :class_methods => {},
+ :c_class_variables => {},
+ :c_singleton_class_variables => {},
+ :encoding => @encoding,
+ :instance_methods => {},
+ :main => nil,
+ :modules => [],
+ :pages => [],
+ :title => nil,
+ }
+
+ @classes_hash = {}
+ @modules_hash = {}
+ @files_hash = {}
+
+ @c_enclosure_classes = {}
+ @c_enclosure_names = {}
+
+ @c_class_variables = {}
+ @c_singleton_class_variables = {}
+
+ @unique_classes = nil
+ @unique_modules = nil
+ end
+
+ ##
+ # Adds +module+ as an enclosure (namespace) for the given +variable+ for C
+ # files.
+
+ def add_c_enclosure variable, namespace
+ @c_enclosure_classes[variable] = namespace
+ end
+
+ ##
+ # Adds C variables from an RDoc::Parser::C
+
+ def add_c_variables c_parser
+ filename = c_parser.top_level.relative_name
+
+ @c_class_variables[filename] = make_variable_map c_parser.classes
+
+ @c_singleton_class_variables[filename] = c_parser.singleton_classes
+ end
+
+ ##
+ # Adds the file with +name+ as an RDoc::TopLevel to the store. Returns the
+ # created RDoc::TopLevel.
+
+ def add_file absolute_name, relative_name = absolute_name
+ unless top_level = @files_hash[relative_name] then
+ top_level = RDoc::TopLevel.new absolute_name, relative_name
+ top_level.store = self
+ @files_hash[relative_name] = top_level
+ end
+
+ top_level
+ end
+
+ ##
+ # Returns all classes discovered by RDoc
+
+ def all_classes
+ @classes_hash.values
+ end
+
+ ##
+ # Returns all classes and modules discovered by RDoc
+
+ def all_classes_and_modules
+ @classes_hash.values + @modules_hash.values
+ end
+
+ ##
+ # All TopLevels known to RDoc
+
+ def all_files
+ @files_hash.values
+ end
+
+ ##
+ # Returns all modules discovered by RDoc
+
+ def all_modules
+ modules_hash.values
+ end
+
+ ##
+ # Ancestors cache accessor. Maps a klass name to an Array of its ancestors
+ # in this store. If Foo in this store inherits from Object, Kernel won't be
+ # listed (it will be included from ruby's ri store).
+
+ def ancestors
+ @cache[:ancestors]
+ end
+
+ ##
+ # Attributes cache accessor. Maps a class to an Array of its attributes.
+
+ def attributes
+ @cache[:attributes]
+ end
+
+ ##
+ # Path to the cache file
+
+ def cache_path
+ File.join @path, 'cache.ri'
+ end
+
+ ##
+ # Path to the ri data for +klass_name+
+
+ def class_file klass_name
+ name = klass_name.split('::').last
+ File.join class_path(klass_name), "cdesc-#{name}.ri"
+ end
+
+ ##
+ # Class methods cache accessor. Maps a class to an Array of its class
+ # methods (not full name).
+
+ def class_methods
+ @cache[:class_methods]
+ end
+
+ ##
+ # Path where data for +klass_name+ will be stored (methods or class data)
+
+ def class_path klass_name
+ File.join @path, *klass_name.split('::')
+ end
+
+ ##
+ # Hash of all classes known to RDoc
+
+ def classes_hash
+ @classes_hash
+ end
+
+ ##
+ # Removes empty items and ensures item in each collection are unique and
+ # sorted
+
+ def clean_cache_collection collection # :nodoc:
+ collection.each do |name, item|
+ if item.empty? then
+ collection.delete name
+ else
+ # HACK mongrel-1.1.5 documents its files twice
+ item.uniq!
+ item.sort!
+ end
+ end
+ end
+
+ ##
+ # Prepares the RDoc code object tree for use by a generator.
+ #
+ # It finds unique classes/modules defined, and replaces classes/modules that
+ # are aliases for another one by a copy with RDoc::ClassModule#is_alias_for
+ # set.
+ #
+ # It updates the RDoc::ClassModule#constant_aliases attribute of "real"
+ # classes or modules.
+ #
+ # It also completely removes the classes and modules that should be removed
+ # from the documentation and the methods that have a visibility below
+ # +min_visibility+, which is the <tt>--visibility</tt> option.
+ #
+ # See also RDoc::Context#remove_from_documentation?
+
+ def complete min_visibility
+ fix_basic_object_inheritance
+
+ # cache included modules before they are removed from the documentation
+ all_classes_and_modules.each { |cm| cm.ancestors }
+
+ unless min_visibility == :nodoc then
+ remove_nodoc @classes_hash
+ remove_nodoc @modules_hash
+ end
+
+ @unique_classes = find_unique @classes_hash
+ @unique_modules = find_unique @modules_hash
+
+ unique_classes_and_modules.each do |cm|
+ cm.complete min_visibility
+ end
+
+ @files_hash.each_key do |file_name|
+ tl = @files_hash[file_name]
+
+ unless tl.text? then
+ tl.modules_hash.clear
+ tl.classes_hash.clear
+
+ tl.classes_or_modules.each do |cm|
+ name = cm.full_name
+ if cm.type == 'class' then
+ tl.classes_hash[name] = cm if @classes_hash[name]
+ else
+ tl.modules_hash[name] = cm if @modules_hash[name]
+ end
+ end
+ end
+ end
+ end
+
+ ##
+ # Hash of all files known to RDoc
+
+ def files_hash
+ @files_hash
+ end
+
+ ##
+ # Finds the enclosure (namespace) for the given C +variable+.
+
+ def find_c_enclosure variable
+ @c_enclosure_classes.fetch variable do
+ break unless name = @c_enclosure_names[variable]
+
+ mod = find_class_or_module name
+
+ unless mod then
+ loaded_mod = load_class_data name
+
+ file = loaded_mod.in_files.first
+
+ return unless file # legacy data source
+
+ file.store = self
+
+ mod = file.add_module RDoc::NormalModule, name
+ end
+
+ @c_enclosure_classes[variable] = mod
+ end
+ end
+
+ ##
+ # Finds the class with +name+ in all discovered classes
+
+ def find_class_named name
+ @classes_hash[name]
+ end
+
+ ##
+ # Finds the class with +name+ starting in namespace +from+
+
+ def find_class_named_from name, from
+ from = find_class_named from unless RDoc::Context === from
+
+ until RDoc::TopLevel === from do
+ return nil unless from
+
+ klass = from.find_class_named name
+ return klass if klass
+
+ from = from.parent
+ end
+
+ find_class_named name
+ end
+
+ ##
+ # Finds the class or module with +name+
+
+ def find_class_or_module name
+ name = $' if name =~ /^::/
+ @classes_hash[name] || @modules_hash[name]
+ end
+
+ ##
+ # Finds the file with +name+ in all discovered files
+
+ def find_file_named name
+ @files_hash[name]
+ end
+
+ ##
+ # Finds the module with +name+ in all discovered modules
+
+ def find_module_named name
+ @modules_hash[name]
+ end
+
+ ##
+ # Returns the RDoc::TopLevel that is a text file and has the given
+ # +file_name+
+
+ def find_text_page file_name
+ @files_hash.each_value.find do |file|
+ file.text? and file.full_name == file_name
+ end
+ end
+
+ ##
+ # Finds unique classes/modules defined in +all_hash+,
+ # and returns them as an array. Performs the alias
+ # updates in +all_hash+: see ::complete.
+ #--
+ # TODO aliases should be registered by Context#add_module_alias
+
+ def find_unique all_hash
+ unique = []
+
+ all_hash.each_pair do |full_name, cm|
+ unique << cm if full_name == cm.full_name
+ end
+
+ unique
+ end
+
+ ##
+ # Fixes the erroneous <tt>BasicObject < Object</tt> in 1.9.
+ #
+ # Because we assumed all classes without a stated superclass
+ # inherit from Object, we have the above wrong inheritance.
+ #
+ # We fix BasicObject right away if we are running in a Ruby
+ # version >= 1.9.
+
+ def fix_basic_object_inheritance
+ basic = classes_hash['BasicObject']
+ return unless basic
+ basic.superclass = nil
+ end
+
+ ##
+ # Friendly rendition of #path
+
+ def friendly_path
+ case type
+ when :gem then
+ parent = File.expand_path '..', @path
+ "gem #{File.basename parent}"
+ when :home then '~/.rdoc'
+ when :site then 'ruby site'
+ when :system then 'ruby core'
+ else @path
+ end
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %s %p>" % [self.class, object_id, @path, module_names.sort]
+ end
+
+ ##
+ # Instance methods cache accessor. Maps a class to an Array of its
+ # instance methods (not full name).
+
+ def instance_methods
+ @cache[:instance_methods]
+ end
+
+ ##
+ # Loads all items from this store into memory. This recreates a
+ # documentation tree for use by a generator
+
+ def load_all
+ load_cache
+
+ module_names.each do |module_name|
+ mod = find_class_or_module(module_name) || load_class(module_name)
+
+ # load method documentation since the loaded class/module does not have
+ # it
+ loaded_methods = mod.method_list.map do |method|
+ load_method module_name, method.full_name
+ end
+
+ mod.method_list.replace loaded_methods
+
+ loaded_attributes = mod.attributes.map do |attribute|
+ load_method module_name, attribute.full_name
+ end
+
+ mod.attributes.replace loaded_attributes
+ end
+
+ all_classes_and_modules.each do |mod|
+ descendent_re = /^#{mod.full_name}::[^:]+$/
+
+ module_names.each do |name|
+ next unless name =~ descendent_re
+
+ descendent = find_class_or_module name
+
+ case descendent
+ when RDoc::NormalClass then
+ mod.classes_hash[name] = descendent
+ when RDoc::NormalModule then
+ mod.modules_hash[name] = descendent
+ end
+ end
+ end
+
+ @cache[:pages].each do |page_name|
+ page = load_page page_name
+ @files_hash[page_name] = page
+ end
+ end
+
+ ##
+ # Loads cache file for this store
+
+ def load_cache
+ #orig_enc = @encoding
+
+ open cache_path, 'rb' do |io|
+ @cache = Marshal.load io.read
+ end
+
+ load_enc = @cache[:encoding]
+
+ # TODO this feature will be time-consuming to add:
+ # a) Encodings may be incompatible but transcodeable
+ # b) Need to warn in the appropriate spots, wherever they may be
+ # c) Need to handle cross-cache differences in encodings
+ # d) Need to warn when generating into a cache with different encodings
+ #
+ #if orig_enc and load_enc != orig_enc then
+ # warn "Cached encoding #{load_enc} is incompatible with #{orig_enc}\n" \
+ # "from #{path}/cache.ri" unless
+ # Encoding.compatible? orig_enc, load_enc
+ #end
+
+ @encoding = load_enc unless @encoding
+
+ @cache[:pages] ||= []
+ @cache[:main] ||= nil
+ @cache[:c_class_variables] ||= {}
+ @cache[:c_singleton_class_variables] ||= {}
+
+ @cache[:c_class_variables].each do |_, map|
+ map.each do |variable, name|
+ @c_enclosure_names[variable] = name
+ end
+ end
+
+ @cache
+ rescue Errno::ENOENT
+ end
+
+ ##
+ # Loads ri data for +klass_name+ and hooks it up to this store.
+
+ def load_class klass_name
+ obj = load_class_data klass_name
+
+ obj.store = self
+
+ case obj
+ when RDoc::NormalClass then
+ @classes_hash[klass_name] = obj
+ when RDoc::NormalModule then
+ @modules_hash[klass_name] = obj
+ end
+ end
+
+ ##
+ # Loads ri data for +klass_name+
+
+ def load_class_data klass_name
+ file = class_file klass_name
+
+ open file, 'rb' do |io|
+ Marshal.load io.read
+ end
+ rescue Errno::ENOENT => e
+ error = MissingFileError.new(self, file, klass_name)
+ error.set_backtrace e.backtrace
+ raise error
+ end
+
+ ##
+ # Loads ri data for +method_name+ in +klass_name+
+
+ def load_method klass_name, method_name
+ file = method_file klass_name, method_name
+
+ open file, 'rb' do |io|
+ obj = Marshal.load io.read
+ obj.store = self
+ obj.parent =
+ find_class_or_module(klass_name) || load_class(klass_name) unless
+ obj.parent
+ obj
+ end
+ rescue Errno::ENOENT => e
+ error = MissingFileError.new(self, file, klass_name + method_name)
+ error.set_backtrace e.backtrace
+ raise error
+ end
+
+ ##
+ # Loads ri data for +page_name+
+
+ def load_page page_name
+ file = page_file page_name
+
+ open file, 'rb' do |io|
+ obj = Marshal.load io.read
+ obj.store = self
+ obj
+ end
+ rescue Errno::ENOENT => e
+ error = MissingFileError.new(self, file, page_name)
+ error.set_backtrace e.backtrace
+ raise error
+ end
+
+ ##
+ # Gets the main page for this RDoc store. This page is used as the root of
+ # the RDoc server.
+
+ def main
+ @cache[:main]
+ end
+
+ ##
+ # Sets the main page for this RDoc store.
+
+ def main= page
+ @cache[:main] = page
+ end
+
+ ##
+ # Converts the variable => ClassModule map +variables+ from a C parser into
+ # a variable => class name map.
+
+ def make_variable_map variables
+ map = {}
+
+ variables.each { |variable, class_module|
+ map[variable] = class_module.full_name
+ }
+
+ map
+ end
+
+ ##
+ # Path to the ri data for +method_name+ in +klass_name+
+
+ def method_file klass_name, method_name
+ method_name = method_name.split('::').last
+ method_name =~ /#(.*)/
+ method_type = $1 ? 'i' : 'c'
+ method_name = $1 if $1
+ method_name = method_name.gsub(/\W/) { "%%%02x" % $&[0].ord }
+
+ File.join class_path(klass_name), "#{method_name}-#{method_type}.ri"
+ end
+
+ ##
+ # Modules cache accessor. An Array of all the module (and class) names in
+ # the store.
+
+ def module_names
+ @cache[:modules]
+ end
+
+ ##
+ # Hash of all modules known to RDoc
+
+ def modules_hash
+ @modules_hash
+ end
+
+ ##
+ # Returns the RDoc::TopLevel that is a text file and has the given +name+
+
+ def page name
+ @files_hash.each_value.find do |file|
+ file.text? and file.page_name == name
+ end
+ end
+
+ ##
+ # Path to the ri data for +page_name+
+
+ def page_file page_name
+ file_name = File.basename(page_name).gsub('.', '_')
+
+ File.join @path, File.dirname(page_name), "page-#{file_name}.ri"
+ end
+
+ ##
+ # Removes from +all_hash+ the contexts that are nodoc or have no content.
+ #
+ # See RDoc::Context#remove_from_documentation?
+
+ def remove_nodoc all_hash
+ all_hash.keys.each do |name|
+ context = all_hash[name]
+ all_hash.delete(name) if context.remove_from_documentation?
+ end
+ end
+
+ ##
+ # Saves all entries in the store
+
+ def save
+ load_cache
+
+ all_classes_and_modules.each do |klass|
+ save_class klass
+
+ klass.each_method do |method|
+ save_method klass, method
+ end
+
+ klass.each_attribute do |attribute|
+ save_method klass, attribute
+ end
+ end
+
+ all_files.each do |file|
+ save_page file
+ end
+
+ save_cache
+ end
+
+ ##
+ # Writes the cache file for this store
+
+ def save_cache
+ clean_cache_collection @cache[:ancestors]
+ clean_cache_collection @cache[:attributes]
+ clean_cache_collection @cache[:class_methods]
+ clean_cache_collection @cache[:instance_methods]
+
+ @cache[:modules].uniq!
+ @cache[:modules].sort!
+
+ @cache[:pages].uniq!
+ @cache[:pages].sort!
+
+ @cache[:encoding] = @encoding # this gets set twice due to assert_cache
+
+ @cache[:c_class_variables].merge! @c_class_variables
+ @cache[:c_singleton_class_variables].merge! @c_singleton_class_variables
+
+ return if @dry_run
+
+ marshal = Marshal.dump @cache
+
+ open cache_path, 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Writes the ri data for +klass+ (or module)
+
+ def save_class klass
+ full_name = klass.full_name
+
+ FileUtils.mkdir_p class_path(full_name) unless @dry_run
+
+ @cache[:modules] << full_name
+
+ path = class_file full_name
+
+ begin
+ disk_klass = load_class full_name
+
+ klass = disk_klass.merge klass
+ rescue MissingFileError
+ end
+
+ # BasicObject has no ancestors
+ ancestors = klass.direct_ancestors.compact.map do |ancestor|
+ # HACK for classes we don't know about (class X < RuntimeError)
+ String === ancestor ? ancestor : ancestor.full_name
+ end
+
+ @cache[:ancestors][full_name] ||= []
+ @cache[:ancestors][full_name].concat ancestors
+
+ attribute_definitions = klass.attributes.map do |attribute|
+ "#{attribute.definition} #{attribute.name}"
+ end
+
+ unless attribute_definitions.empty? then
+ @cache[:attributes][full_name] ||= []
+ @cache[:attributes][full_name].concat attribute_definitions
+ end
+
+ to_delete = []
+
+ unless klass.method_list.empty? then
+ @cache[:class_methods][full_name] ||= []
+ @cache[:instance_methods][full_name] ||= []
+
+ class_methods, instance_methods =
+ klass.method_list.partition { |meth| meth.singleton }
+
+ class_methods = class_methods. map { |method| method.name }
+ instance_methods = instance_methods.map { |method| method.name }
+ attribute_names = klass.attributes.map { |attr| attr.name }
+
+ old = @cache[:class_methods][full_name] - class_methods
+ to_delete.concat old.map { |method|
+ method_file full_name, "#{full_name}::#{method}"
+ }
+
+ old = @cache[:instance_methods][full_name] -
+ instance_methods - attribute_names
+ to_delete.concat old.map { |method|
+ method_file full_name, "#{full_name}##{method}"
+ }
+
+ @cache[:class_methods][full_name] = class_methods
+ @cache[:instance_methods][full_name] = instance_methods
+ end
+
+ return if @dry_run
+
+ FileUtils.rm_f to_delete
+
+ marshal = Marshal.dump klass
+
+ open path, 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Writes the ri data for +method+ on +klass+
+
+ def save_method klass, method
+ full_name = klass.full_name
+
+ FileUtils.mkdir_p class_path(full_name) unless @dry_run
+
+ cache = if method.singleton then
+ @cache[:class_methods]
+ else
+ @cache[:instance_methods]
+ end
+ cache[full_name] ||= []
+ cache[full_name] << method.name
+
+ return if @dry_run
+
+ marshal = Marshal.dump method
+
+ open method_file(full_name, method.full_name), 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Writes the ri data for +page+
+
+ def save_page page
+ return unless page.text?
+
+ path = page_file page.full_name
+
+ FileUtils.mkdir_p File.dirname(path) unless @dry_run
+
+ cache[:pages] ||= []
+ cache[:pages] << page.full_name
+
+ return if @dry_run
+
+ marshal = Marshal.dump page
+
+ open path, 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Source of the contents of this store.
+ #
+ # For a store from a gem the source is the gem name. For a store from the
+ # home directory the source is "home". For system ri store (the standard
+ # library documentation) the source is"ruby". For a store from the site
+ # ri directory the store is "site". For other stores the source is the
+ # #path.
+
+ def source
+ case type
+ when :gem then File.basename File.expand_path '..', @path
+ when :home then 'home'
+ when :site then 'site'
+ when :system then 'ruby'
+ else @path
+ end
+ end
+
+ ##
+ # Gets the title for this RDoc store. This is used as the title in each
+ # page on the RDoc server
+
+ def title
+ @cache[:title]
+ end
+
+ ##
+ # Sets the title page for this RDoc store.
+
+ def title= title
+ @cache[:title] = title
+ end
+
+ ##
+ # Returns the unique classes discovered by RDoc.
+ #
+ # ::complete must have been called prior to using this method.
+
+ def unique_classes
+ @unique_classes
+ end
+
+ ##
+ # Returns the unique classes and modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def unique_classes_and_modules
+ @unique_classes + @unique_modules
+ end
+
+ ##
+ # Returns the unique modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def unique_modules
+ @unique_modules
+ end
+
+end
diff --git a/lib/rdoc/task.rb b/lib/rdoc/task.rb
new file mode 100644
index 0000000000..323d00eabc
--- /dev/null
+++ b/lib/rdoc/task.rb
@@ -0,0 +1,329 @@
+# frozen_string_literal: true
+#--
+# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#++
+
+begin
+ gem 'rdoc'
+rescue Gem::LoadError
+end unless defined?(RDoc)
+
+begin
+ gem 'rake'
+rescue Gem::LoadError
+end unless defined?(Rake)
+
+require 'rdoc'
+require 'rake'
+require 'rake/tasklib'
+
+##
+# RDoc::Task creates the following rake tasks to generate and clean up RDoc
+# output:
+#
+# [rdoc]
+# Main task for this RDoc task.
+#
+# [clobber_rdoc]
+# Delete all the rdoc files. This target is automatically added to the main
+# clobber target.
+#
+# [rerdoc]
+# Rebuild the rdoc files from scratch, even if they are not out of date.
+#
+# Simple Example:
+#
+# require 'rdoc/task'
+#
+# RDoc::Task.new do |rdoc|
+# rdoc.main = "README.rdoc"
+# rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+# end
+#
+# The +rdoc+ object passed to the block is an RDoc::Task object. See the
+# attributes list for the RDoc::Task class for available customization options.
+#
+# == Specifying different task names
+#
+# You may wish to give the task a different name, such as if you are
+# generating two sets of documentation. For instance, if you want to have a
+# development set of documentation including private methods:
+#
+# require 'rdoc/task'
+#
+# RDoc::Task.new :rdoc_dev do |rdoc|
+# rdoc.main = "README.doc"
+# rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+# rdoc.options << "--all"
+# end
+#
+# The tasks would then be named :<em>rdoc_dev</em>,
+# :clobber_<em>rdoc_dev</em>, and :re<em>rdoc_dev</em>.
+#
+# If you wish to have completely different task names, then pass a Hash as
+# first argument. With the <tt>:rdoc</tt>, <tt>:clobber_rdoc</tt> and
+# <tt>:rerdoc</tt> options, you can customize the task names to your liking.
+#
+# For example:
+#
+# require 'rdoc/task'
+#
+# RDoc::Task.new(:rdoc => "rdoc", :clobber_rdoc => "rdoc:clean",
+# :rerdoc => "rdoc:force")
+#
+# This will create the tasks <tt>:rdoc</tt>, <tt>:rdoc:clean</tt> and
+# <tt>:rdoc:force</tt>.
+
+class RDoc::Task < Rake::TaskLib
+
+ ##
+ # Name of the main, top level task. (default is :rdoc)
+
+ attr_accessor :name
+
+ ##
+ # Comment markup format. rdoc, rd and tomdoc are supported. (default is
+ # 'rdoc')
+
+ attr_accessor :markup
+
+ ##
+ # Name of directory to receive the html output files. (default is "html")
+
+ attr_accessor :rdoc_dir
+
+ ##
+ # Title of RDoc documentation. (defaults to rdoc's default)
+
+ attr_accessor :title
+
+ ##
+ # Name of file to be used as the main, top level file of the RDoc. (default
+ # is none)
+
+ attr_accessor :main
+
+ ##
+ # Name of template to be used by rdoc. (defaults to rdoc's default)
+
+ attr_accessor :template
+
+ ##
+ # Name of format generator (<tt>--format<tt>) used by rdoc. (defaults to
+ # rdoc's default)
+
+ attr_accessor :generator
+
+ ##
+ # List of files to be included in the rdoc generation. (default is [])
+
+ attr_accessor :rdoc_files
+
+ ##
+ # Additional list of options to be passed rdoc. (default is [])
+
+ attr_accessor :options
+
+ ##
+ # Whether to run the rdoc process as an external shell (default is false)
+
+ attr_accessor :external
+
+ ##
+ # Create an RDoc task with the given name. See the RDoc::Task class overview
+ # for documentation.
+
+ def initialize name = :rdoc # :yield: self
+ defaults
+
+ check_names name
+
+ @name = name
+
+ yield self if block_given?
+
+ define
+ end
+
+ ##
+ # Ensures that +names+ only includes names for the :rdoc, :clobber_rdoc and
+ # :rerdoc. If other names are given an ArgumentError is raised.
+
+ def check_names names
+ return unless Hash === names
+
+ invalid_options =
+ names.keys.map { |k| k.to_sym } - [:rdoc, :clobber_rdoc, :rerdoc]
+
+ unless invalid_options.empty? then
+ raise ArgumentError, "invalid options: #{invalid_options.join ', '}"
+ end
+ end
+
+ ##
+ # Task description for the clobber rdoc task or its renamed equivalent
+
+ def clobber_task_description
+ "Remove RDoc HTML files"
+ end
+
+ ##
+ # Sets default task values
+
+ def defaults
+ @name = :rdoc
+ @rdoc_files = Rake::FileList.new
+ @rdoc_dir = 'html'
+ @main = nil
+ @title = nil
+ @template = nil
+ @generator = nil
+ @options = []
+ end
+
+ ##
+ # All source is inline now. This method is deprecated
+
+ def inline_source # :nodoc:
+ warn "RDoc::Task#inline_source is deprecated"
+ true
+ end
+
+ ##
+ # All source is inline now. This method is deprecated
+
+ def inline_source=(value) # :nodoc:
+ warn "RDoc::Task#inline_source is deprecated"
+ end
+
+ ##
+ # Create the tasks defined by this task lib.
+
+ def define
+ desc rdoc_task_description
+ task rdoc_task_name
+
+ desc rerdoc_task_description
+ task rerdoc_task_name => [clobber_task_name, rdoc_task_name]
+
+ desc clobber_task_description
+ task clobber_task_name do
+ rm_r @rdoc_dir rescue nil
+ end
+
+ task :clobber => [clobber_task_name]
+
+ directory @rdoc_dir
+
+ rdoc_target_deps = [
+ @rdoc_files,
+ Rake.application.rakefile
+ ].flatten.compact
+
+ task rdoc_task_name => [rdoc_target]
+ file rdoc_target => rdoc_target_deps do
+ @before_running_rdoc.call if @before_running_rdoc
+ args = option_list + @rdoc_files
+
+ $stderr.puts "rdoc #{args.join ' '}" if Rake.application.options.trace
+ RDoc::RDoc.new.document args
+ end
+
+ self
+ end
+
+ ##
+ # List of options that will be supplied to RDoc
+
+ def option_list
+ result = @options.dup
+ result << "-o" << @rdoc_dir
+ result << "--main" << main if main
+ result << "--markup" << markup if markup
+ result << "--title" << title if title
+ result << "-T" << template if template
+ result << '-f' << generator if generator
+ result
+ end
+
+ ##
+ # The block passed to this method will be called just before running the
+ # RDoc generator. It is allowed to modify RDoc::Task attributes inside the
+ # block.
+
+ def before_running_rdoc(&block)
+ @before_running_rdoc = block
+ end
+
+ ##
+ # Task description for the rdoc task or its renamed equivalent
+
+ def rdoc_task_description
+ 'Build RDoc HTML files'
+ end
+
+ ##
+ # Task description for the rerdoc task or its renamed description
+
+ def rerdoc_task_description
+ "Rebuild RDoc HTML files"
+ end
+
+ private
+
+ def rdoc_target
+ "#{rdoc_dir}/created.rid"
+ end
+
+ def rdoc_task_name
+ case name
+ when Hash then (name[:rdoc] || "rdoc").to_s
+ else name.to_s
+ end
+ end
+
+ def clobber_task_name
+ case name
+ when Hash then (name[:clobber_rdoc] || "clobber_rdoc").to_s
+ else "clobber_#{name}"
+ end
+ end
+
+ def rerdoc_task_name
+ case name
+ when Hash then (name[:rerdoc] || "rerdoc").to_s
+ else "re#{name}"
+ end
+ end
+
+end
+
+# :stopdoc:
+module Rake
+
+ ##
+ # For backwards compatibility
+
+ RDocTask = RDoc::Task
+
+end
+# :startdoc:
diff --git a/lib/rdoc/template.rb b/lib/rdoc/template.rb
deleted file mode 100644
index 469e10fb4b..0000000000
--- a/lib/rdoc/template.rb
+++ /dev/null
@@ -1,234 +0,0 @@
-# Cheap-n-cheerful HTML page template system. You create a
-# template containing:
-#
-# * variable names between percent signs (<tt>%fred%</tt>)
-# * blocks of repeating stuff:
-#
-# START:key
-# ... stuff
-# END:key
-#
-# You feed the code a hash. For simple variables, the values
-# are resolved directly from the hash. For blocks, the hash entry
-# corresponding to +key+ will be an array of hashes. The block will
-# be generated once for each entry. Blocks can be nested arbitrarily
-# deeply.
-#
-# The template may also contain
-#
-# IF:key
-# ... stuff
-# ENDIF:key
-#
-# _stuff_ will only be included in the output if the corresponding
-# key is set in the value hash.
-#
-# Usage: Given a set of templates <tt>T1, T2,</tt> etc
-#
-# values = { "name" => "Dave", state => "TX" }
-#
-# t = TemplatePage.new(T1, T2, T3)
-# File.open(name, "w") {|f| t.write_html_on(f, values)}
-# or
-# res = ''
-# t.write_html_on(res, values)
-#
-#
-
-class TemplatePage
-
- ##########
- # A context holds a stack of key/value pairs (like a symbol
- # table). When asked to resolve a key, it first searches the top of
- # the stack, then the next level, and so on until it finds a match
- # (or runs out of entries)
-
- class Context
- def initialize
- @stack = []
- end
-
- def push(hash)
- @stack.push(hash)
- end
-
- def pop
- @stack.pop
- end
-
- # Find a scalar value, throwing an exception if not found. This
- # method is used when substituting the %xxx% constructs
-
- def find_scalar(key)
- @stack.reverse_each do |level|
- if val = level[key]
- return val unless val.kind_of? Array
- end
- end
- raise "Template error: can't find variable '#{key}'"
- end
-
- # Lookup any key in the stack of hashes
-
- def lookup(key)
- @stack.reverse_each do |level|
- val = level[key]
- return val if val
- end
- nil
- end
- end
-
- #########
- # Simple class to read lines out of a string
-
- class LineReader
- # we're initialized with an array of lines
- def initialize(lines)
- @lines = lines
- end
-
- # read the next line
- def read
- @lines.shift
- end
-
- # Return a list of lines up to the line that matches
- # a pattern. That last line is discarded.
- def read_up_to(pattern)
- res = []
- while line = read
- if pattern.match(line)
- return LineReader.new(res)
- else
- res << line
- end
- end
- raise "Missing end tag in template: #{pattern.source}"
- end
-
- # Return a copy of ourselves that can be modified without
- # affecting us
- def dup
- LineReader.new(@lines.dup)
- end
- end
-
-
-
- # +templates+ is an array of strings containing the templates.
- # We start at the first, and substitute in subsequent ones
- # where the string <tt>!INCLUDE!</tt> occurs. For example,
- # we could have the overall page template containing
- #
- # <html><body>
- # <h1>Master</h1>
- # !INCLUDE!
- # </bost></html>
- #
- # and substitute subpages in to it by passing [master, sub_page].
- # This gives us a cheap way of framing pages
-
- def initialize(*templates)
- result = "!INCLUDE!"
- templates.each do |content|
- result.sub!(/!INCLUDE!/, content)
- end
- @lines = LineReader.new(result.split($/))
- end
-
- # Render the templates into HTML, storing the result on +op+
- # using the method <tt><<</tt>. The <tt>value_hash</tt> contains
- # key/value pairs used to drive the substitution (as described above)
-
- def write_html_on(op, value_hash)
- @context = Context.new
- op << substitute_into(@lines, value_hash).tr("\000", '\\')
- end
-
-
- # Substitute a set of key/value pairs into the given template.
- # Keys with scalar values have them substituted directly into
- # the page. Those with array values invoke <tt>substitute_array</tt>
- # (below), which examples a block of the template once for each
- # row in the array.
- #
- # This routine also copes with the <tt>IF:</tt>_key_ directive,
- # removing chunks of the template if the corresponding key
- # does not appear in the hash, and the START: directive, which
- # loops its contents for each value in an array
-
- def substitute_into(lines, values)
- @context.push(values)
- skip_to = nil
- result = []
-
- while line = lines.read
-
- case line
-
- when /^IF:(\w+)/
- lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
-
- when /^IFNOT:(\w+)/
- lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
-
- when /^ENDIF:/
- ;
-
- when /^START:(\w+)/
- tag = $1
- body = lines.read_up_to(/^END:#{tag}/)
- inner_values = @context.lookup(tag)
- raise "unknown tag: #{tag}" unless inner_values
- raise "not array: #{tag}" unless inner_values.kind_of?(Array)
- inner_values.each do |vals|
- result << substitute_into(body.dup, vals)
- end
- else
- result << expand_line(line.dup)
- end
- end
-
- @context.pop
-
- result.join("\n")
- end
-
- # Given an individual line, we look for %xxx% constructs and
- # HREF:ref:name: constructs, substituting for each.
-
- def expand_line(line)
- # Generate a cross reference if a reference is given,
- # otherwise just fill in the name part
-
- line.gsub!(/HREF:(\w+?):(\w+?):/) {
- ref = @context.lookup($1)
- name = @context.find_scalar($2)
-
- if ref and !ref.kind_of?(Array)
- "<a href=\"#{ref}\">#{name}</a>"
- else
- name
- end
- }
-
- # Substitute in values for %xxx% constructs. This is made complex
- # because the replacement string may contain characters that are
- # meaningful to the regexp (like \1)
-
- line = line.gsub(/%([a-zA-Z]\w*)%/) {
- val = @context.find_scalar($1)
- val.tr('\\', "\000")
- }
-
-
- line
- rescue Exception => e
- $stderr.puts "Error in template: #{e}"
- $stderr.puts "Original line: #{line}"
- exit
- end
-
-end
-
diff --git a/lib/rdoc/test_case.rb b/lib/rdoc/test_case.rb
new file mode 100644
index 0000000000..22d3f14219
--- /dev/null
+++ b/lib/rdoc/test_case.rb
@@ -0,0 +1,203 @@
+# frozen_string_literal: true
+begin
+ gem 'minitest', '~> 4.0' unless defined?(Test::Unit)
+rescue NoMethodError, Gem::LoadError
+ # for ruby tests
+end
+
+require 'minitest/autorun'
+require 'minitest/benchmark' unless ENV['NOBENCHMARK']
+
+require 'fileutils'
+require 'pp'
+require 'tempfile'
+require 'tmpdir'
+require 'stringio'
+
+require 'rdoc'
+
+##
+# RDoc::TestCase is an abstract TestCase to provide common setup and teardown
+# across all RDoc tests. The test case uses minitest, so all the assertions
+# of minitest may be used.
+#
+# The testcase provides the following:
+#
+# * A reset code-object tree
+# * A reset markup preprocessor (RDoc::Markup::PreProcess)
+# * The <code>@RM</code> alias of RDoc::Markup (for less typing)
+# * <code>@pwd</code> containing the current working directory
+# * FileUtils, pp, Tempfile, Dir.tmpdir and StringIO
+
+class RDoc::TestCase < MiniTest::Unit::TestCase
+
+ ##
+ # Abstract test-case setup
+
+ def setup
+ super
+
+ @top_level = nil
+
+ @RM = RDoc::Markup
+
+ RDoc::Markup::PreProcess.reset
+
+ @pwd = Dir.pwd
+
+ @store = RDoc::Store.new
+
+ @rdoc = RDoc::RDoc.new
+ @rdoc.store = @store
+ @rdoc.options = RDoc::Options.new
+
+ g = Object.new
+ def g.class_dir() end
+ def g.file_dir() end
+ @rdoc.generator = g
+ end
+
+ ##
+ # Asserts +path+ is a file
+
+ def assert_file path
+ assert File.file?(path), "#{path} is not a file"
+ end
+
+ ##
+ # Asserts +path+ is a directory
+
+ def assert_directory path
+ assert File.directory?(path), "#{path} is not a directory"
+ end
+
+ ##
+ # Refutes +path+ exists
+
+ def refute_file path
+ refute File.exist?(path), "#{path} exists"
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::BlankLine.new
+
+ def blank_line
+ @RM::BlankLine.new
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::BlockQuote.new with +contents+
+
+ def block *contents
+ @RM::BlockQuote.new(*contents)
+ end
+
+ ##
+ # Creates an RDoc::Comment with +text+ which was defined on +top_level+.
+ # By default the comment has the 'rdoc' format.
+
+ def comment text, top_level = @top_level
+ RDoc::Comment.new text, top_level
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Document.new with +contents+
+
+ def doc *contents
+ @RM::Document.new(*contents)
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::HardBreak.new
+
+ def hard_break
+ @RM::HardBreak.new
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Heading.new with +level+ and +text+
+
+ def head level, text
+ @RM::Heading.new level, text
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::ListItem.new with +label+ and +parts+
+
+ def item label = nil, *parts
+ @RM::ListItem.new label, *parts
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::List.new with +type+ and +items+
+
+ def list type = nil, *items
+ @RM::List.new type, *items
+ end
+
+ ##
+ # Enables pretty-print output
+
+ def mu_pp obj # :nodoc:
+ s = obj.pretty_inspect
+ s = RDoc::Encoding.change_encoding s, Encoding.default_external
+ s.chomp
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Paragraph.new with +contents+
+
+ def para *a
+ @RM::Paragraph.new(*a)
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Rule.new with +weight+
+
+ def rule weight
+ @RM::Rule.new weight
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Raw.new with +contents+
+
+ def raw *contents
+ @RM::Raw.new(*contents)
+ end
+
+ ##
+ # Creates a temporary directory changes the current directory to it for the
+ # duration of the block.
+ #
+ # Depends upon Dir.mktmpdir
+
+ def temp_dir
+ Dir.mktmpdir do |temp_dir|
+ Dir.chdir temp_dir do
+ yield temp_dir
+ end
+ end
+ end
+
+ ##
+ # Shortcut for RDoc::Markup::Verbatim.new with +parts+
+
+ def verb *parts
+ @RM::Verbatim.new(*parts)
+ end
+
+ ##
+ # run capture_io with setting $VERBOSE = true
+
+ def verbose_capture_io
+ capture_io do
+ begin
+ orig_verbose = $VERBOSE
+ $VERBOSE = true
+ yield
+ ensure
+ $VERBOSE = orig_verbose
+ end
+ end
+ end
+end
diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb
new file mode 100644
index 0000000000..7e714be0ad
--- /dev/null
+++ b/lib/rdoc/text.rb
@@ -0,0 +1,298 @@
+# frozen_string_literal: true
+
+##
+# For RDoc::Text#to_html
+
+require 'strscan'
+
+##
+# Methods for manipulating comment text
+
+module RDoc::Text
+
+ ##
+ # Maps markup formats to classes that can parse them. If the format is
+ # unknown, "rdoc" format is used.
+
+ MARKUP_FORMAT = {
+ 'markdown' => RDoc::Markdown,
+ 'rdoc' => RDoc::Markup,
+ 'rd' => RDoc::RD,
+ 'tomdoc' => RDoc::TomDoc,
+ }
+
+ MARKUP_FORMAT.default = RDoc::Markup
+
+ ##
+ # Maps an encoding to a Hash of characters properly transcoded for that
+ # encoding.
+ #
+ # See also encode_fallback.
+
+ TO_HTML_CHARACTERS = Hash.new do |h, encoding|
+ h[encoding] = {
+ :close_dquote => encode_fallback('”', encoding, '"'),
+ :close_squote => encode_fallback('’', encoding, '\''),
+ :copyright => encode_fallback('©', encoding, '(c)'),
+ :ellipsis => encode_fallback('…', encoding, '...'),
+ :em_dash => encode_fallback('—', encoding, '---'),
+ :en_dash => encode_fallback('–', encoding, '--'),
+ :open_dquote => encode_fallback('“', encoding, '"'),
+ :open_squote => encode_fallback('‘', encoding, '\''),
+ :trademark => encode_fallback('®', encoding, '(r)'),
+ }
+ end
+
+ ##
+ # Transcodes +character+ to +encoding+ with a +fallback+ character.
+
+ def self.encode_fallback character, encoding, fallback
+ character.encode(encoding, :fallback => { character => fallback },
+ :undef => :replace, :replace => fallback)
+ end
+
+ ##
+ # Expands tab characters in +text+ to eight spaces
+
+ def expand_tabs text
+ expanded = []
+
+ text.each_line do |line|
+ nil while line.gsub!(/(?:\G|\r)((?:.{8})*?)([^\t\r\n]{0,7})\t/) do
+ r = "#{$1}#{$2}#{' ' * (8 - $2.size)}"
+ r = RDoc::Encoding.change_encoding r, text.encoding
+ r
+ end
+
+ expanded << line
+ end
+
+ expanded.join
+ end
+
+ ##
+ # Flush +text+ left based on the shortest line
+
+ def flush_left text
+ indent = 9999
+
+ text.each_line do |line|
+ line_indent = line =~ /\S/ || 9999
+ indent = line_indent if indent > line_indent
+ end
+
+ empty = ''
+ empty = RDoc::Encoding.change_encoding empty, text.encoding
+
+ text.gsub(/^ {0,#{indent}}/, empty)
+ end
+
+ ##
+ # Convert a string in markup format into HTML.
+ #
+ # Requires the including class to implement #formatter
+
+ def markup text
+ if @store.rdoc.options
+ locale = @store.rdoc.options.locale
+ else
+ locale = nil
+ end
+ if locale
+ i18n_text = RDoc::I18n::Text.new(text)
+ text = i18n_text.translate(locale)
+ end
+ parse(text).accept formatter
+ end
+
+ ##
+ # Strips hashes, expands tabs then flushes +text+ to the left
+
+ def normalize_comment text
+ return text if text.empty?
+
+ text = strip_stars text
+ text = strip_hashes text
+ text = expand_tabs text
+ text = flush_left text
+ text = strip_newlines text
+ text
+ end
+
+ ##
+ # Normalizes +text+ then builds a RDoc::Markup::Document from it
+
+ def parse text, format = 'rdoc'
+ return text if RDoc::Markup::Document === text
+ return text.parse if RDoc::Comment === text
+
+ text = normalize_comment text # TODO remove, should not be necessary
+
+ return RDoc::Markup::Document.new if text =~ /\A\n*\z/
+
+ MARKUP_FORMAT[format].parse text
+ end
+
+ ##
+ # The first +limit+ characters of +text+ as HTML
+
+ def snippet text, limit = 100
+ document = parse text
+
+ RDoc::Markup::ToHtmlSnippet.new(options, limit).convert document
+ end
+
+ ##
+ # Strips leading # characters from +text+
+
+ def strip_hashes text
+ return text if text =~ /^(?>\s*)[^\#]/
+
+ empty = ''
+ empty = RDoc::Encoding.change_encoding empty, text.encoding
+
+ text.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }.gsub(/^\s+$/, empty)
+ end
+
+ ##
+ # Strips leading and trailing \n characters from +text+
+
+ def strip_newlines text
+ text.gsub(/\A\n*(.*?)\n*\z/m) do $1 end # block preserves String encoding
+ end
+
+ ##
+ # Strips /* */ style comments
+
+ def strip_stars text
+ return text unless text =~ %r%/\*.*\*/%m
+
+ encoding = text.encoding
+
+ text = text.gsub %r%Document-method:\s+[\w:.#=!?]+%, ''
+
+ space = ' '
+ space = RDoc::Encoding.change_encoding space, encoding if encoding
+
+ text.sub! %r%/\*+% do space * $&.length end
+ text.sub! %r%\*+/% do space * $&.length end
+ text.gsub! %r%^[ \t]*\*%m do space * $&.length end
+
+ empty = ''
+ empty = RDoc::Encoding.change_encoding empty, encoding if encoding
+ text.gsub(/^\s+$/, empty)
+ end
+
+ ##
+ # Converts ampersand, dashes, ellipsis, quotes, copyright and registered
+ # trademark symbols in +text+ to properly encoded characters.
+
+ def to_html text
+ html = (''.encode text.encoding).dup
+
+ encoded = RDoc::Text::TO_HTML_CHARACTERS[text.encoding]
+
+ s = StringScanner.new text
+ insquotes = false
+ indquotes = false
+ after_word = nil
+
+ until s.eos? do
+ case
+ when s.scan(/<(tt|code)>.*?<\/\1>/) then # skip contents of tt
+ html << s.matched.gsub('\\\\', '\\')
+ when s.scan(/<(tt|code)>.*?/) then
+ warn "mismatched <#{s[1]}> tag" # TODO signal file/line
+ html << s.matched
+ when s.scan(/<[^>]+\/?s*>/) then # skip HTML tags
+ html << s.matched
+ when s.scan(/\\(\S)/) then # unhandled suppressed crossref
+ html << s[1]
+ after_word = nil
+ when s.scan(/\.\.\.(\.?)/) then
+ html << s[1] << encoded[:ellipsis]
+ after_word = nil
+ when s.scan(/\(c\)/) then
+ html << encoded[:copyright]
+ after_word = nil
+ when s.scan(/\(r\)/) then
+ html << encoded[:trademark]
+ after_word = nil
+ when s.scan(/---/) then
+ html << encoded[:em_dash]
+ after_word = nil
+ when s.scan(/--/) then
+ html << encoded[:en_dash]
+ after_word = nil
+ when s.scan(/&quot;|"/) then
+ html << encoded[indquotes ? :close_dquote : :open_dquote]
+ indquotes = !indquotes
+ after_word = nil
+ when s.scan(/``/) then # backtick double quote
+ html << encoded[:open_dquote]
+ after_word = nil
+ when s.scan(/''/) then # tick double quote
+ html << encoded[:close_dquote]
+ after_word = nil
+ when s.scan(/'/) then # single quote
+ if insquotes
+ html << encoded[:close_squote]
+ insquotes = false
+ elsif after_word
+ # Mary's dog, my parents' house: do not start paired quotes
+ html << encoded[:close_squote]
+ else
+ html << encoded[:open_squote]
+ insquotes = true
+ end
+
+ after_word = nil
+ else # advance to the next potentially significant character
+ match = s.scan(/.+?(?=[<\\.("'`&-])/) #"
+
+ if match then
+ html << match
+ after_word = match =~ /\w$/
+ else
+ html << s.rest
+ break
+ end
+ end
+ end
+
+ html
+ end
+
+ ##
+ # Wraps +txt+ to +line_len+
+
+ 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.join.strip
+ end
+
+end
diff --git a/lib/rdoc/token_stream.rb b/lib/rdoc/token_stream.rb
new file mode 100644
index 0000000000..05fb46e89a
--- /dev/null
+++ b/lib/rdoc/token_stream.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+##
+# A TokenStream is a list of tokens, gathered during the parse of some entity
+# (say a method). Entities populate these streams by being registered with the
+# lexer. Any class can collect tokens by including TokenStream. From the
+# outside, you use such an object by calling the start_collecting_tokens
+# method, followed by calls to add_token and pop_token.
+
+module RDoc::TokenStream
+
+ ##
+ # Converts +token_stream+ to HTML wrapping various tokens with
+ # <tt><span></tt> elements. Some tokens types are wrapped in spans
+ # with the given class names. Other token types are not wrapped in spans.
+
+ def self.to_html token_stream
+ starting_title = false
+
+ token_stream.map do |t|
+ next unless t
+
+ style = case t[:kind]
+ when :on_const then 'ruby-constant'
+ when :on_kw then 'ruby-keyword'
+ when :on_ivar then 'ruby-ivar'
+ when :on_cvar then 'ruby-identifier'
+ when :on_gvar then 'ruby-identifier'
+ when '=' != t[:text] && :on_op
+ then 'ruby-operator'
+ when :on_tlambda then 'ruby-operator'
+ when :on_ident then 'ruby-identifier'
+ when :on_label then 'ruby-value'
+ when :on_backref, :on_dstring
+ then 'ruby-node'
+ when :on_comment then 'ruby-comment'
+ when :on_embdoc then 'ruby-comment'
+ when :on_regexp then 'ruby-regexp'
+ when :on_tstring then 'ruby-string'
+ when :on_int, :on_float,
+ :on_rational, :on_imaginary,
+ :on_heredoc,
+ :on_symbol, :on_CHAR then 'ruby-value'
+ when :on_heredoc_beg, :on_heredoc_end
+ then 'ruby-identifier'
+ end
+
+ comment_with_nl = false
+ if :on_comment == t[:kind] or :on_embdoc == t[:kind] or :on_heredoc_end == t[:kind]
+ comment_with_nl = true if "\n" == t[:text][-1]
+ text = t[:text].rstrip
+ else
+ text = t[:text]
+ end
+
+ if :on_ident == t[:kind] && starting_title
+ starting_title = false
+ style = 'ruby-identifier ruby-title'
+ end
+
+ if :on_kw == t[:kind] and 'def' == t[:text]
+ starting_title = true
+ end
+
+ text = CGI.escapeHTML text
+
+ if style then
+ "<span class=\"#{style}\">#{text}</span>#{"\n" if comment_with_nl}"
+ else
+ text
+ end
+ end.join
+ end
+
+ ##
+ # Adds +tokens+ to the collected tokens
+
+ def add_tokens(*tokens)
+ tokens.flatten.each { |token| @token_stream << token }
+ end
+
+ alias add_token add_tokens
+
+ ##
+ # Starts collecting tokens
+
+ def collect_tokens
+ @token_stream = []
+ end
+
+ alias start_collecting_tokens collect_tokens
+
+ ##
+ # Remove the last token from the collected tokens
+
+ def pop_token
+ @token_stream.pop
+ end
+
+ ##
+ # Current token stream
+
+ def token_stream
+ @token_stream
+ end
+
+ ##
+ # Returns a string representation of the token stream
+
+ def tokens_to_s
+ token_stream.compact.map { |token| token.text }.join ''
+ end
+
+end
+
diff --git a/lib/rdoc/tokenstream.rb b/lib/rdoc/tokenstream.rb
deleted file mode 100644
index 0a0720d8a9..0000000000
--- a/lib/rdoc/tokenstream.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# A TokenStream is a list of tokens, gathered during the parse
-# of some entity (say a method). Entities populate these streams
-# by being registered with the lexer. Any class can collect tokens
-# by including TokenStream. From the outside, you use such an object
-# by calling the start_collecting_tokens method, followed by calls
-# to add_token and pop_token
-
-module TokenStream
- def token_stream
- @token_stream
- end
-
- def start_collecting_tokens
- @token_stream = []
- end
- def add_token(tk)
- @token_stream << tk
- end
- def add_tokens(tks)
- tks.each {|tk| add_token(tk)}
- end
- def pop_token
- @token_stream.pop
- end
-end
diff --git a/lib/rdoc/tom_doc.rb b/lib/rdoc/tom_doc.rb
new file mode 100644
index 0000000000..2b594b7d84
--- /dev/null
+++ b/lib/rdoc/tom_doc.rb
@@ -0,0 +1,258 @@
+# frozen_string_literal: true
+# :markup: tomdoc
+
+# A parser for TomDoc based on TomDoc 1.0.0-rc1 (02adef9b5a)
+#
+# The TomDoc specification can be found at:
+#
+# http://tomdoc.org
+#
+# The latest version of the TomDoc specification can be found at:
+#
+# https://github.com/mojombo/tomdoc/blob/master/tomdoc.md
+#
+# To choose TomDoc as your only default format see RDoc::Options@Saved+Options
+# for instructions on setting up a <code>.rdoc_options</code> file to store
+# your project default.
+#
+# There are a few differences between this parser and the specification. A
+# best-effort was made to follow the specification as closely as possible but
+# some choices to deviate were made.
+#
+# A future version of RDoc will warn when a MUST or MUST NOT is violated and
+# may warn when a SHOULD or SHOULD NOT is violated. RDoc will always try
+# to emit documentation even if given invalid TomDoc.
+#
+# Here are some implementation choices this parser currently makes:
+#
+# This parser allows rdoc-style inline markup but you should not depended on
+# it.
+#
+# This parser allows a space between the comment and the method body.
+#
+# This parser does not require the default value to be described for an
+# optional argument.
+#
+# This parser does not examine the order of sections. An Examples section may
+# precede the Arguments section.
+#
+# This class is documented in TomDoc format. Since this is a subclass of the
+# RDoc markup parser there isn't much to see here, unfortunately.
+
+class RDoc::TomDoc < RDoc::Markup::Parser
+
+ # Internal: Token accessor
+
+ attr_reader :tokens
+
+ # Internal: Adds a post-processor which sets the RDoc section based on the
+ # comment's status.
+ #
+ # Returns nothing.
+
+ def self.add_post_processor # :nodoc:
+ RDoc::Markup::PreProcess.post_process do |comment, code_object|
+ next unless code_object and
+ RDoc::Comment === comment and comment.format == 'tomdoc'
+
+ comment.text.gsub!(/(\A\s*# )(Public|Internal|Deprecated):\s+/) do
+ section = code_object.add_section $2
+ code_object.temporary_section = section
+
+ $1
+ end
+ end
+ end
+
+ add_post_processor
+
+ # Public: Parses TomDoc from text
+ #
+ # text - A String containing TomDoc-format text.
+ #
+ # Examples
+ #
+ # RDoc::TomDoc.parse <<-TOMDOC
+ # This method does some things
+ #
+ # Returns nothing.
+ # TOMDOC
+ # # => #<RDoc::Markup::Document:0xXXX @parts=[...], @file=nil>
+ #
+ # Returns an RDoc::Markup::Document representing the TomDoc format.
+
+ def self.parse text
+ parser = new
+
+ parser.tokenize text
+ doc = RDoc::Markup::Document.new
+ parser.parse doc
+ doc
+ end
+
+ # Internal: Extracts the Signature section's method signature
+ #
+ # comment - An RDoc::Comment that will be parsed and have the signature
+ # extracted
+ #
+ # Returns a String containing the signature and nil if not
+
+ def self.signature comment
+ return unless comment.tomdoc?
+
+ document = comment.parse
+
+ signature = nil
+ found_heading = false
+ found_signature = false
+
+ document.parts.delete_if do |part|
+ next false if found_signature
+
+ found_heading ||=
+ RDoc::Markup::Heading === part && part.text == 'Signature'
+
+ next false unless found_heading
+
+ next true if RDoc::Markup::BlankLine === part
+
+ if RDoc::Markup::Verbatim === part then
+ signature = part
+ found_signature = true
+ end
+ end
+
+ signature and signature.text
+ end
+
+ # Public: Creates a new TomDoc parser. See also RDoc::Markup::parse
+
+ def initialize
+ super
+
+ @section = nil
+ @seen_returns = false
+ end
+
+ # Internal: Builds a heading from the token stream
+ #
+ # level - The level of heading to create
+ #
+ # Returns an RDoc::Markup::Heading
+
+ def build_heading level
+ heading = super
+
+ @section = heading.text
+
+ heading
+ end
+
+ # Internal: Builds a verbatim from the token stream. A verbatim in the
+ # Examples section will be marked as in Ruby format.
+ #
+ # margin - The indentation from the margin for lines that belong to this
+ # verbatim section.
+ #
+ # Returns an RDoc::Markup::Verbatim
+
+ def build_verbatim margin
+ verbatim = super
+
+ verbatim.format = :ruby if @section == 'Examples'
+
+ verbatim
+ end
+
+ # Internal: Builds a paragraph from the token stream
+ #
+ # margin - Unused
+ #
+ # Returns an RDoc::Markup::Paragraph.
+
+ def build_paragraph margin
+ p :paragraph_start => margin if @debug
+
+ paragraph = RDoc::Markup::Paragraph.new
+
+ until @tokens.empty? do
+ type, data, = get
+
+ case type
+ when :TEXT then
+ @section = 'Returns' if data =~ /\AReturns/
+
+ paragraph << data
+ when :NEWLINE then
+ if :TEXT == peek_token[0] then
+ paragraph << ' '
+ else
+ break
+ end
+ else
+ unget
+ break
+ end
+ end
+
+ p :paragraph_end => margin if @debug
+
+ paragraph
+ end
+
+ ##
+ # Detects a section change to "Returns" and adds a heading
+
+ def parse_text parent, indent # :nodoc:
+ paragraph = build_paragraph indent
+
+ if false == @seen_returns and 'Returns' == @section then
+ @seen_returns = true
+ parent << RDoc::Markup::Heading.new(3, 'Returns')
+ parent << RDoc::Markup::BlankLine.new
+ end
+
+ parent << paragraph
+ end
+
+ # Internal: Turns text into an Array of tokens
+ #
+ # text - A String containing TomDoc-format text.
+ #
+ # Returns self.
+
+ def tokenize text
+ text = text.sub(/\A(Public|Internal|Deprecated):\s+/, '')
+
+ setup_scanner text
+
+ until @s.eos? do
+ pos = @s.pos
+
+ # leading spaces will be reflected by the column of the next token
+ # the only thing we loose are trailing spaces at the end of the file
+ next if @s.scan(/ +/)
+
+ @tokens << case
+ when @s.scan(/\r?\n/) then
+ token = [:NEWLINE, @s.matched, *token_pos(pos)]
+ @line_pos = char_pos @s.pos
+ @line += 1
+ token
+ when @s.scan(/(Examples|Signature)$/) then
+ @tokens << [:HEADER, 3, *token_pos(pos)]
+
+ [:TEXT, @s[1], *token_pos(pos)]
+ when @s.scan(/([:\w][\w\[\]]*)[ ]+- /) then
+ [:NOTE, @s[1], *token_pos(pos)]
+ else
+ @s.scan(/.*/)
+ [:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)]
+ end
+ end
+
+ self
+ end
+
+end
+
diff --git a/lib/rdoc/top_level.rb b/lib/rdoc/top_level.rb
new file mode 100644
index 0000000000..6186722772
--- /dev/null
+++ b/lib/rdoc/top_level.rb
@@ -0,0 +1,283 @@
+# frozen_string_literal: true
+##
+# A TopLevel context is a representation of the contents of a single file
+
+class RDoc::TopLevel < RDoc::Context
+
+ MARSHAL_VERSION = 0 # :nodoc:
+
+ ##
+ # This TopLevel's File::Stat struct
+
+ attr_accessor :file_stat
+
+ ##
+ # Relative name of this file
+
+ attr_accessor :relative_name
+
+ ##
+ # Absolute name of this file
+
+ attr_accessor :absolute_name
+
+ ##
+ # All the classes or modules that were declared in
+ # this file. These are assigned to either +#classes_hash+
+ # or +#modules_hash+ once we know what they really are.
+
+ attr_reader :classes_or_modules
+
+ attr_accessor :diagram # :nodoc:
+
+ ##
+ # The parser class that processed this file
+
+ attr_accessor :parser
+
+ ##
+ # Creates a new TopLevel for the file at +absolute_name+. If documentation
+ # is being generated outside the source dir +relative_name+ is relative to
+ # the source directory.
+
+ def initialize absolute_name, relative_name = absolute_name
+ super()
+ @name = nil
+ @absolute_name = absolute_name
+ @relative_name = relative_name
+ @file_stat = File.stat(absolute_name) rescue nil # HACK for testing
+ @diagram = nil
+ @parser = nil
+
+ @classes_or_modules = []
+ end
+
+ ##
+ # An RDoc::TopLevel is equal to another with the same relative_name
+
+ def == other
+ self.class === other and @relative_name == other.relative_name
+ end
+
+ alias eql? ==
+
+ ##
+ # Adds +an_alias+ to +Object+ instead of +self+.
+
+ def add_alias(an_alias)
+ object_class.record_location self
+ return an_alias unless @document_self
+ object_class.add_alias an_alias
+ end
+
+ ##
+ # Adds +constant+ to +Object+ instead of +self+.
+
+ def add_constant constant
+ object_class.record_location self
+ return constant unless @document_self
+ object_class.add_constant constant
+ end
+
+ ##
+ # Adds +include+ to +Object+ instead of +self+.
+
+ def add_include(include)
+ object_class.record_location self
+ return include unless @document_self
+ object_class.add_include include
+ end
+
+ ##
+ # Adds +method+ to +Object+ instead of +self+.
+
+ def add_method(method)
+ object_class.record_location self
+ return method unless @document_self
+ object_class.add_method method
+ end
+
+ ##
+ # Adds class or module +mod+. Used in the building phase
+ # by the Ruby parser.
+
+ def add_to_classes_or_modules mod
+ @classes_or_modules << mod
+ end
+
+ ##
+ # Base name of this file
+
+ def base_name
+ File.basename @relative_name
+ end
+
+ alias name base_name
+
+ ##
+ # Only a TopLevel that contains text file) will be displayed. See also
+ # RDoc::CodeObject#display?
+
+ def display?
+ text? and super
+ end
+
+ ##
+ # See RDoc::TopLevel::find_class_or_module
+ #--
+ # TODO Why do we search through all classes/modules found, not just the
+ # ones of this instance?
+
+ def find_class_or_module name
+ @store.find_class_or_module name
+ end
+
+ ##
+ # Finds a class or module named +symbol+
+
+ def find_local_symbol(symbol)
+ find_class_or_module(symbol) || super
+ end
+
+ ##
+ # Finds a module or class with +name+
+
+ def find_module_named(name)
+ find_class_or_module(name)
+ end
+
+ ##
+ # Returns the relative name of this file
+
+ def full_name
+ @relative_name
+ end
+
+ ##
+ # An RDoc::TopLevel has the same hash as another with the same
+ # relative_name
+
+ def hash
+ @relative_name.hash
+ end
+
+ ##
+ # URL for this with a +prefix+
+
+ def http_url(prefix)
+ path = [prefix, @relative_name.tr('.', '_')]
+
+ File.join(*path.compact) + '.html'
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %p modules: %p classes: %p>" % [
+ self.class, object_id,
+ base_name,
+ @modules.map { |n,m| m },
+ @classes.map { |n,c| c }
+ ]
+ end
+
+ ##
+ # Time this file was last modified, if known
+
+ def last_modified
+ @file_stat ? file_stat.mtime : nil
+ end
+
+ ##
+ # Dumps this TopLevel for use by ri. See also #marshal_load
+
+ def marshal_dump
+ [
+ MARSHAL_VERSION,
+ @relative_name,
+ @parser,
+ parse(@comment),
+ ]
+ end
+
+ ##
+ # Loads this TopLevel from +array+.
+
+ def marshal_load array # :nodoc:
+ initialize array[1]
+
+ @parser = array[2]
+ @comment = array[3]
+
+ @file_stat = nil
+ end
+
+ ##
+ # Returns the NormalClass "Object", creating it if not found.
+ #
+ # Records +self+ as a location in "Object".
+
+ def object_class
+ @object_class ||= begin
+ oc = @store.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
+ oc.record_location self
+ oc
+ end
+ end
+
+ ##
+ # Base name of this file without the extension
+
+ def page_name
+ basename = File.basename @relative_name
+ basename =~ /\.(rb|rdoc|txt|md)$/i
+
+ $` || basename
+ end
+
+ ##
+ # Path to this file for use with HTML generator output.
+
+ def path
+ http_url @store.rdoc.generator.file_dir
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, "[#{self.class}: ", "]" do
+ q.text "base name: #{base_name.inspect}"
+ q.breakable
+
+ items = @modules.map { |n,m| m }
+ items.concat @modules.map { |n,c| c }
+ q.seplist items do |mod| q.pp mod end
+ end
+ end
+
+ ##
+ # Search record used by RDoc::Generator::JsonIndex
+
+ def search_record
+ return unless @parser < RDoc::Parser::Text
+
+ [
+ page_name,
+ '',
+ page_name,
+ '',
+ path,
+ '',
+ snippet(@comment),
+ ]
+ end
+
+ ##
+ # Is this TopLevel from a text file instead of a source code file?
+
+ def text?
+ @parser and @parser.ancestors.include? RDoc::Parser::Text
+ end
+
+ def to_s # :nodoc:
+ "file #{full_name}"
+ end
+
+end
+
diff --git a/lib/rdoc/usage.rb b/lib/rdoc/usage.rb
deleted file mode 100644
index def516b3d7..0000000000
--- a/lib/rdoc/usage.rb
+++ /dev/null
@@ -1,210 +0,0 @@
-# = Synopsis
-#
-# This library allows command-line tools to encapsulate their usage
-# as a comment at the top of the main file. Calling <tt>RDoc::usage</tt>
-# then displays some or all of that comment, and optionally exits
-# the program with an exit status. We always look for the comment
-# in the main program file, so it is safe to call this method
-# from anywhere in the executing program.
-#
-# = Usage
-#
-# RDoc::usage( [ exit_status ], [ section, ...])
-# RDoc::usage_no_exit( [ section, ...])
-#
-# where:
-#
-# exit_status::
-# the integer exit code (default zero). RDoc::usage will exit
-# the calling program with this status.
-#
-# section::
-# an optional list of section names. If specified, only the
-# sections with the given names as headings will be output.
-# For example, this section is named 'Usage', and the next
-# section is named 'Examples'. The section names are case
-# insensitive.
-#
-# = Examples
-#
-# # Comment block describing usage
-# # with (optional) section headings
-# # . . .
-#
-# require 'rdoc/usage'
-#
-# # Display all usage and exit with a status of 0
-#
-# RDoc::usage
-#
-# # Display all usage and exit with a status of 99
-#
-# RDoc::usage(99)
-#
-# # Display usage in the 'Summary' section only, then
-# # exit with a status of 99
-#
-# RDoc::usage(99, 'Summary')
-#
-# # Display information in the Author and Copyright
-# # sections, then exit 0.
-#
-# RDoc::usage('Author', 'Copyright')
-#
-# # Display information in the Author and Copyright
-# # sections, but don't exit
-#
-# RDoc::usage_no_exit('Author', 'Copyright')
-#
-# = Author
-#
-# Dave Thomas, The Pragmatic Programmers, LLC
-#
-# = Copyright
-#
-# Copyright (c) 2004 Dave Thomas.
-# Licensed under the same terms as Ruby
-#
-
-require 'rdoc/markup/simple_markup'
-require 'rdoc/markup/simple_markup/to_flow'
-require 'rdoc/ri/ri_formatter'
-require 'rdoc/ri/ri_options'
-
-module RDoc
-
- # Display usage information from the comment at the top of
- # the file. String arguments identify specific sections of the
- # comment to display. An optional integer first argument
- # specifies the exit status (defaults to 0)
-
- def RDoc.usage(*args)
- exit_code = 0
-
- if args.size > 0
- status = args[0]
- if status.respond_to?(:to_int)
- exit_code = status.to_int
- args.shift
- end
- end
-
- # display the usage and exit with the given code
- usage_no_exit(*args)
- exit(exit_code)
- end
-
- # Display usage
- def RDoc.usage_no_exit(*args)
- main_program_file = caller[-1].sub(/:\d+$/, '')
- comment = File.open(main_program_file) do |file|
- find_comment(file)
- end
-
- comment = comment.gsub(/^\s*#/, '')
-
- markup = SM::SimpleMarkup.new
- flow_convertor = SM::ToFlow.new
-
- flow = markup.convert(comment, flow_convertor)
-
- format = "plain"
-
- unless args.empty?
- flow = extract_sections(flow, args)
- end
-
- options = RI::Options.instance
- if args = ENV["RI"]
- options.parse(args.split)
- end
- formatter = options.formatter.new(options, "")
- formatter.display_flow(flow)
- end
-
- ######################################################################
-
- private
-
- # Find the first comment in the file (that isn't a shebang line)
- # If the file doesn't start with a comment, report the fact
- # and return empty string
-
- def RDoc.gets(file)
- if (line = file.gets) && (line =~ /^#!/) # shebang
- throw :exit, find_comment(file)
- else
- line
- end
- end
-
- def RDoc.find_comment(file)
- catch(:exit) do
- # skip leading blank lines
- 0 while (line = gets(file)) && (line =~ /^\s*$/)
-
- comment = []
- while line && line =~ /^\s*#/
- comment << line
- line = gets(file)
- end
-
- 0 while line && (line = gets(file))
- return no_comment if comment.empty?
- return comment.join
- end
- end
-
-
- #####
- # Given an array of flow items and an array of section names, extract those
- # sections from the flow which have headings corresponding to
- # a section name in the list. Return them in the order
- # of names in the +sections+ array.
-
- def RDoc.extract_sections(flow, sections)
- result = []
- sections.each do |name|
- name = name.downcase
- copy_upto_level = nil
-
- flow.each do |item|
- case item
- when SM::Flow::H
- if copy_upto_level && item.level >= copy_upto_level
- copy_upto_level = nil
- else
- if item.text.downcase == name
- result << item
- copy_upto_level = item.level
- end
- end
- else
- if copy_upto_level
- result << item
- end
- end
- end
- end
- if result.empty?
- puts "Note to developer: requested section(s) [#{sections.join(', ')}] " +
- "not found"
- result = flow
- end
- result
- end
-
- #####
- # Report the fact that no doc comment count be found
- def RDoc.no_comment
- $stderr.puts "No usage information available for this program"
- ""
- end
-end
-
-
-if $0 == __FILE__
-
- RDoc::usage(*ARGV)
-
-end