* lib/rdoc*: Updated to RDoc 4.0 (pre-release)

* bin/rdoc:  ditto
* test/rdoc:  ditto
* NEWS:  Updated with RDoc 4.0 information


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37889 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2012-11-27 04:28:14 +00:00
Родитель c72f0daa87
Коммит 1c279a7d27
233 изменённых файлов: 45019 добавлений и 5100 удалений

Просмотреть файл

@ -1,3 +1,10 @@
Tue Nov 27 13:27:46 2012 Eric Hodel <drbrain@segment7.net>
* lib/rdoc*: Updated to RDoc 4.0 (pre-release)
* bin/rdoc: ditto
* test/rdoc*: ditto
* NEWS: Updated with RDoc 4.0 information
Tue Nov 27 12:17:11 2012 Koichi Sasada <ko1@atdot.net>
* thread.c (rb_thread_terminate_all): retry broadcast only when

11
NEWS
Просмотреть файл

@ -272,6 +272,17 @@ with all sufficient information, see the ChangeLog file.
http://rake.rubyforge.org/doc/release_notes/rake-0_9_4_rdoc.html for a list
of changes in rake 0.9.3 and 0.9.4.
* rdoc
* rdoc has been updated to version 4.0
This version is largely backwards-compatible with previous rdoc versions.
The most notable change is an update to the ri data format (ri data must
be regenerated for gems shared across rdoc versions). Further API changes
are internal and won't affect most users.
See https://github.com/rdoc/rdoc/blob/master/History.rdoc for a list of
changes in rdoc 4.0.
* resolv
* new methods:
* Resolv::DNS#timeouts=

Просмотреть файл

@ -5,8 +5,6 @@
#
# Copyright (c) 2003 Dave Thomas
# Released under the same terms as Ruby
#
# $Revision$
begin
gem 'rdoc'
@ -20,6 +18,10 @@ require 'rdoc/rdoc'
begin
r = RDoc::RDoc.new
r.document ARGV
rescue Errno::ENOSPC
$stderr.puts 'Ran out of space creating documentation'
$stderr.puts
$stderr.puts 'Please free up some space and try again'
rescue SystemExit
raise
rescue Exception => e

Просмотреть файл

@ -1,7 +1,7 @@
/*
* utf8tbl.c - Convertion Table for nkf
*
* $Id: utf8tbl.c,v 1.23 2008/02/07 19:25:29 naruse Exp $
* $Id$
*/
#include "config.h"

Просмотреть файл

@ -1,86 +1,55 @@
$DEBUG_RDOC = nil
# :main: README.txt
# :main: README.rdoc
##
# RDoc is a Ruby documentation system which contains RDoc::RDoc for generating
# documentation, RDoc::RI for interactive documentation and RDoc::Markup for
# text markup.
# RDoc produces documentation for Ruby source files by parsing the source and
# extracting the definition for classes, modules, methods, includes and
# requires. It associates these with optional documentation contained in an
# immediately preceding comment block then renders the result using an output
# formatter.
#
# RDoc::RDoc produces documentation for Ruby source files. It works similarly
# to JavaDoc, parsing the source and extracting the definition for classes,
# modules, methods, includes and requires. It associates these with optional
# documentation contained in an immediately preceding comment block then
# renders the result using an output formatter.
#
# RDoc::Markup 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.
#
# RDoc::RI implements the +ri+ command-line tool which displays on-line
# documentation for ruby classes, methods, etc. +ri+ features several output
# formats and an interactive mode (<tt>ri -i</tt>). See <tt>ri --help</tt>
# for further details.
# For a simple introduction to writing or generating documentation using RDoc
# see the README.
#
# == Roadmap
#
# * If you want to use RDoc to create documentation for your Ruby source files,
# see RDoc::Markup and refer to <tt>rdoc --help</tt> for command line
# usage.
# * If you want to write documentation for Ruby files see RDoc::Parser::Ruby
# * If you want to write documentation for extensions written in C see
# RDoc::Parser::C
# * If you want to generate documentation using <tt>rake</tt> see RDoc::Task.
# * If you want to drive RDoc programmatically, see RDoc::RDoc.
# * If you want to use the library to format text blocks into HTML, look at
# RDoc::Markup.
# * If you want to make an RDoc plugin such as a generator or directive
# handler see RDoc::RDoc.
# * If you want to write your own output generator see RDoc::Generator.
# If you think you found a bug in RDoc see DEVELOPERS@Bugs
#
# == Summary
# If you want to use RDoc to create documentation for your Ruby source files,
# see RDoc::Markup and refer to <tt>rdoc --help</tt> for command line usage.
#
# Once installed, you can create documentation using the +rdoc+ command
# If you want to set the default markup format see
# RDoc::Markup@Supported+Formats
#
# % rdoc [options] [names...]
# If you want to store rdoc configuration in your gem (such as the default
# markup format) see RDoc::Options@Saved+Options
#
# For an up-to-date option summary, type
# If you want to write documentation for Ruby files see RDoc::Parser::Ruby
#
# % rdoc --help
# If you want to write documentation for extensions written in C see
# RDoc::Parser::C
#
# A typical use might be to generate documentation for a package of Ruby
# source (such as RDoc itself).
# If you want to generate documentation using <tt>rake</tt> see RDoc::Task.
#
# % rdoc
# If you want to drive RDoc programmatically, see RDoc::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+.
# If you want to use the library to format text blocks into HTML or other
# formats, look at RDoc::Markup.
#
# 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
# If you want to make an RDoc plugin such as a generator or directive handler
# see RDoc::RDoc.
#
# % rdoc --main README.txt
# If you want to write your own output generator see RDoc::Generator.
#
# You'll find information on the various formatting tricks you can use
# in comment blocks in the documentation this generates.
# If you want an overview of how RDoc works see DEVELOPERS
#
# RDoc uses file extensions to determine how to process each file. File names
# ending +.rb+ and +.rbw+ are assumed to be Ruby source. Files
# ending +.c+ are parsed as C files. All other files are assumed to
# contain just Markup-style markup (with or without leading '#' comment
# markers). If directory names are passed to RDoc, they are scanned
# recursively for C and Ruby source files only.
#
# == Other stuff
# == Credits
#
# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>.
#
# Dave Thomas <dave@pragmaticprogrammer.com> is the original author of RDoc.
#
# == 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.
@ -92,19 +61,10 @@ module RDoc
class Error < RuntimeError; end
def self.const_missing const_name # :nodoc:
if const_name.to_s == 'RDocError' then
warn "RDoc::RDocError is deprecated"
return Error
end
super
end
##
# RDoc version you are using
VERSION = '3.9.4'
VERSION = '4.0'
##
# Method visibilities
@ -143,5 +103,80 @@ module RDoc
METHOD_MODIFIERS = GENERAL_MODIFIERS +
%w[arg args yield yields notnew not-new not_new doc]
##
# Loads the best available YAML library.
def self.load_yaml
begin
gem 'psych'
rescue Gem::LoadError
end
begin
require 'psych'
rescue ::LoadError
ensure
require 'yaml'
end
end
autoload :RDoc, 'rdoc/rdoc'
autoload :TestCase, 'rdoc/test_case'
autoload :CrossReference, 'rdoc/cross_reference'
autoload :ERBIO, 'rdoc/erbio'
autoload :ERBPartial, 'rdoc/erb_partial'
autoload :Encoding, 'rdoc/encoding'
autoload :Generator, 'rdoc/generator'
autoload :Options, 'rdoc/options'
autoload :Parser, 'rdoc/parser'
autoload :Servlet, 'rdoc/servlet'
autoload :RI, 'rdoc/ri'
autoload :Stats, 'rdoc/stats'
autoload :Store, 'rdoc/store'
autoload :Task, 'rdoc/task'
autoload :Text, 'rdoc/text'
autoload :Markdown, 'rdoc/markdown'
autoload :Markup, 'rdoc/markup'
autoload :RD, 'rdoc/rd'
autoload :TomDoc, 'rdoc/tom_doc'
autoload :KNOWN_CLASSES, 'rdoc/known_classes'
autoload :RubyLex, 'rdoc/ruby_lex'
autoload :RubyToken, 'rdoc/ruby_token'
autoload :TokenStream, 'rdoc/token_stream'
autoload :Comment, 'rdoc/comment'
# code objects
#
# We represent the various high-level code constructs that appear in Ruby
# programs: classes, modules, methods, and so on.
autoload :CodeObject, 'rdoc/code_object'
autoload :Context, 'rdoc/context'
autoload :TopLevel, 'rdoc/top_level'
autoload :AnonClass, 'rdoc/anon_class'
autoload :ClassModule, 'rdoc/class_module'
autoload :NormalClass, 'rdoc/normal_class'
autoload :NormalModule, 'rdoc/normal_module'
autoload :SingleClass, 'rdoc/single_class'
autoload :Alias, 'rdoc/alias'
autoload :AnyMethod, 'rdoc/any_method'
autoload :MethodAttr, 'rdoc/method_attr'
autoload :GhostMethod, 'rdoc/ghost_method'
autoload :MetaMethod, 'rdoc/meta_method'
autoload :Attr, 'rdoc/attr'
autoload :Constant, 'rdoc/constant'
autoload :Include, 'rdoc/include'
autoload :Extend, 'rdoc/extend'
autoload :Require, 'rdoc/require'
end

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/code_object'
##
# Represent an alias, which is an old_name/new_name pair associated with a
# particular context

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/class_module'
##
# An anonymous class like:
#

Просмотреть файл

@ -1,12 +1,16 @@
require 'rdoc/method_attr'
require 'rdoc/token_stream'
##
# AnyMethod is the base class for objects representing methods
class RDoc::AnyMethod < RDoc::MethodAttr
MARSHAL_VERSION = 1 # :nodoc:
##
# 2::
# RDoc 4
# Added calls_super
# Added parent name and class
# Added section title
MARSHAL_VERSION = 2 # :nodoc:
##
# Don't rename \#initialize to \::new
@ -28,6 +32,11 @@ class RDoc::AnyMethod < RDoc::MethodAttr
attr_accessor :params
##
# If true this method uses +super+ to call a superclass version
attr_accessor :calls_super
include RDoc::TokenStream
##
@ -39,6 +48,8 @@ class RDoc::AnyMethod < RDoc::MethodAttr
@c_function = nil
@dont_rename_initialize = false
@token_stream = nil
@calls_super = false
@superclass_method = nil
end
##
@ -97,6 +108,10 @@ class RDoc::AnyMethod < RDoc::MethodAttr
aliases,
@params,
@file.absolute_name,
@calls_super,
@parent.name,
@parent.class,
@section.title,
]
end
@ -107,34 +122,44 @@ class RDoc::AnyMethod < RDoc::MethodAttr
# * #full_name
# * #parent_name
def marshal_load(array)
def marshal_load array
@dont_rename_initialize = nil
@is_alias_for = 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]
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]
array[8].each do |new_name, comment|
add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton)
end
@params = array[9]
@parent_name = if @full_name =~ /#/ then
$`
else
name = @full_name.split('::')
name.pop
name.join '::'
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
@ -169,7 +194,9 @@ class RDoc::AnyMethod < RDoc::MethodAttr
return []
end
params.gsub(/\s+/, '').split ','
params = params.gsub(/\s+/, '').split ','
params.map { |param| param.sub(/=.*/, '') }
end
##
@ -181,10 +208,12 @@ class RDoc::AnyMethod < RDoc::MethodAttr
params = @call_seq.split("\n").last
params = params.sub(/[^( ]+/, '')
params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2')
else
elsif @params then
params = @params.gsub(/\s*\#.*/, '')
params = params.tr("\n", " ").squeeze(" ")
params = "(#{params})" unless params[0] == ?(
else
params = ''
end
if @block_params then
@ -203,5 +232,31 @@ class RDoc::AnyMethod < RDoc::MethodAttr
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

Просмотреть файл

@ -1,12 +1,16 @@
require 'rdoc/method_attr'
##
# An attribute created by \#attr, \#attr_reader, \#attr_writer or
# \#attr_accessor
class RDoc::Attr < RDoc::MethodAttr
MARSHAL_VERSION = 2 # :nodoc:
##
# 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')?
@ -57,6 +61,16 @@ class RDoc::Attr < RDoc::MethodAttr
'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.
@ -93,6 +107,9 @@ class RDoc::Attr < RDoc::MethodAttr
parse(@comment),
singleton,
@file.absolute_name,
@parent.full_name,
@parent.class,
@section.title
]
end
@ -104,17 +121,28 @@ class RDoc::Attr < RDoc::MethodAttr
# * #parent_name
def marshal_load array
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
@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
@parent_name ||= @full_name.split('#', 2).first
end
def pretty_print q # :nodoc:
@ -132,5 +160,14 @@ class RDoc::Attr < RDoc::MethodAttr
"#{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

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/context'
##
# ClassModule is the base class for objects representing either a class or a
# module.
@ -13,8 +11,17 @@ class RDoc::ClassModule < RDoc::Context
# * 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 = 1 # :nodoc:
MARSHAL_VERSION = 3 # :nodoc:
##
# Constants that are aliases for this class or module
@ -56,6 +63,7 @@ class RDoc::ClassModule < RDoc::Context
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
@ -84,6 +92,7 @@ class RDoc::ClassModule < RDoc::Context
klass.external_aliases +
klass.constants +
klass.includes +
klass.extends +
klass.classes +
klass.modules).each do |obj|
obj.parent = klass
@ -115,16 +124,32 @@ class RDoc::ClassModule < RDoc::Context
# across multiple runs.
def add_comment comment, location
return if comment.empty? or not document_self
return unless document_self
original = comment
comment = normalize_comment comment
comment = case comment
when RDoc::Comment then
comment.normalize
else
normalize_comment comment
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).
@ -141,6 +166,11 @@ class RDoc::ClassModule < RDoc::Context
includes.map { |i| i.module }.reverse
end
##
# Ancestors of this class or module only
alias direct_ancestors ancestors
##
# Clears the comment. Used by the ruby parser.
@ -155,9 +185,13 @@ class RDoc::ClassModule < RDoc::Context
# more like <tt>+=</tt>.
def comment= comment
return if comment.empty?
comment = case comment
when RDoc::Comment then
comment.normalize
else
normalize_comment comment
end
comment = normalize_comment comment
comment = "#{@comment}\n---\n#{comment}" unless @comment.empty?
super comment
@ -166,7 +200,7 @@ class RDoc::ClassModule < RDoc::Context
##
# Prepares this ClassModule for use by a generator.
#
# See RDoc::TopLevel::complete
# See RDoc::Store#complete
def complete min_visibility
update_aliases
@ -175,13 +209,23 @@ class RDoc::ClassModule < RDoc::Context
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
##
# 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
@ -215,8 +259,8 @@ class RDoc::ClassModule < RDoc::Context
# Return the fully qualified name of this class or module
def full_name
@full_name ||= if RDoc::ClassModule === @parent then
"#{@parent.full_name}::#{@name}"
@full_name ||= if RDoc::ClassModule === parent then
"#{parent.full_name}::#{@name}"
else
@name
end
@ -250,13 +294,20 @@ class RDoc::ClassModule < RDoc::Context
@superclass,
parse(@comment_location),
attrs,
constants.map do |const|
[const.name, parse(const.comment), const.file_name]
end,
constants,
includes.map do |incl|
[incl.name, parse(incl.comment), incl.file_name]
end,
method_types,
extends.map do |ext|
[ext.name, parse(ext.comment), ext.file_name]
end,
@sections.values,
@in_files.map do |tl|
tl.absolute_name
end,
parent.full_name,
parent.class,
]
end
@ -268,6 +319,8 @@ class RDoc::ClassModule < RDoc::Context
@parent = nil
@temporary_section = nil
@visibility = nil
@classes = {}
@modules = {}
@name = array[1]
@full_name = array[2]
@ -291,9 +344,14 @@ class RDoc::ClassModule < RDoc::Context
attr.record_location RDoc::TopLevel.new file
end
array[6].each do |name, comment, file|
const = add_constant RDoc::Constant.new(name, nil, comment)
const.record_location RDoc::TopLevel.new file
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|
@ -313,6 +371,27 @@ class RDoc::ClassModule < RDoc::Context
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
##
@ -321,6 +400,9 @@ class RDoc::ClassModule < RDoc::Context
# 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
@ -360,6 +442,18 @@ class RDoc::ClassModule < RDoc::Context
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
@ -369,6 +463,8 @@ class RDoc::ClassModule < RDoc::Context
end
end
merge_sections cm
self
end
@ -391,22 +487,46 @@ class RDoc::ClassModule < RDoc::Context
my_things = mine. group_by { |thing| thing.file }
other_things = other.group_by { |thing| thing.file }
my_things.delete_if do |file, things|
next false unless other_files.include? file
remove_things my_things, other_files, &block
add_things my_things, other_things, &block
end
things.each do |thing|
yield false, thing
end
##
# Merges the comments in this ClassModule with the comments in the other
# ClassModule +cm+.
true
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_things.each do |file, things|
my_things[file].each { |thing| yield false, thing } if
my_things.include?(file)
other_sections.each do |group, sections|
if my_sections.include? group
my_sections[group].each do |my_section|
other_section = cm.sections_hash[group]
things.each do |thing|
yield true, thing
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
@ -438,11 +558,15 @@ class RDoc::ClassModule < RDoc::Context
when Array then
docs = comment_location.map do |comment, location|
doc = super comment
doc.file = location.absolute_name
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
@ -451,10 +575,10 @@ class RDoc::ClassModule < RDoc::Context
end
##
# Path to this class or module
# Path to this class or module for use with HTML generator output.
def path
http_url RDoc::RDoc.current.generator.class_dir
http_url @store.rdoc.generator.class_dir
end
##
@ -488,21 +612,61 @@ class RDoc::ClassModule < RDoc::Context
modules_hash.each_key do |name|
full_name = prefix + name
modules_hash.delete name unless RDoc::TopLevel.all_modules_hash[full_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 RDoc::TopLevel.all_classes_hash[full_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
RDoc::TopLevel.find_class_named(@superclass) || @superclass
@store.find_class_named(@superclass) || @superclass
end
##
@ -533,7 +697,7 @@ class RDoc::ClassModule < RDoc::Context
# aliases through a constant.
#
# The aliased module/class is replaced in the children and in
# RDoc::TopLevel::all_modules_hash or RDoc::TopLevel::all_classes_hash
# 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.
@ -548,16 +712,21 @@ class RDoc::ClassModule < RDoc::Context
next unless cm = const.is_alias_for
cm_alias = cm.dup
cm_alias.name = const.name
cm_alias.parent = self
cm_alias.full_name = nil # force update for new parent
# 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
RDoc::TopLevel.all_modules_hash[cm_alias.full_name] = cm_alias
@store.modules_hash[cm_alias.full_name] = cm_alias
modules_hash[const.name] = cm_alias
else
RDoc::TopLevel.all_classes_hash[cm_alias.full_name] = cm_alias
@store.classes_hash[cm_alias.full_name] = cm_alias
classes_hash[const.name] = cm_alias
end
@ -574,8 +743,26 @@ class RDoc::ClassModule < RDoc::Context
def update_includes
includes.reject! do |include|
mod = include.module
!(String === mod) && RDoc::TopLevel.all_modules_hash[mod.full_name].nil?
!(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

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc'
require 'rdoc/text'
##
# Base class for the RDoc code tree.
#
@ -78,9 +75,9 @@ class RDoc::CodeObject
attr_accessor :offset
##
# Our parent CodeObject
# Sets the parent CodeObject
attr_accessor :parent
attr_writer :parent
##
# Did we ever receive a +:nodoc:+ directive?
@ -88,9 +85,14 @@ class RDoc::CodeObject
attr_reader :received_nodoc
##
# Which section are we in
# Set the section this CodeObject is in
attr_accessor :section
attr_writer :section
##
# The RDoc::Store for this object.
attr_accessor :store
##
# We are the model of the code, but we know that at some point we will be
@ -103,11 +105,16 @@ class RDoc::CodeObject
# Creates a new CodeObject that will document itself and its children
def initialize
@metadata = {}
@comment = ''
@parent = nil
@file = nil
@full_name = nil
@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
@document_children = true
@document_self = true
@ -124,11 +131,11 @@ class RDoc::CodeObject
@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
# TODO is this sufficient?
# HACK correct fix is to have #initialize create @comment
# with the correct encoding
if String === @comment and
@ -216,7 +223,7 @@ class RDoc::CodeObject
##
# Force the documentation of this object unless documentation
# has been turned off by :endoc:
# has been turned off by :enddoc:
#--
# HACK untested, was assigning to an ivar
@ -261,6 +268,29 @@ class RDoc::CodeObject
@ignored
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
@ -283,9 +313,19 @@ class RDoc::CodeObject
@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 :endoc:
# turned off by :enddoc:
def start_doc
return if @done_documenting

Просмотреть файл

@ -1,23 +1,5 @@
# We represent the various high-level code constructs that appear in Ruby
# programs: classes, modules, methods, and so on.
# This file was used to load all the RDoc::CodeObject subclasses at once. Now
# autoload handles this.
require 'rdoc/code_object'
require 'rdoc/context'
require 'rdoc/top_level'
require 'rdoc/class_module'
require 'rdoc/normal_class'
require 'rdoc/normal_module'
require 'rdoc/anon_class'
require 'rdoc/single_class'
require 'rdoc/any_method'
require 'rdoc/alias'
require 'rdoc/ghost_method'
require 'rdoc/meta_method'
require 'rdoc/attr'
require 'rdoc/constant'
require 'rdoc/require'
require 'rdoc/include'
require 'rdoc'

232
lib/rdoc/comment.rb Normal file
Просмотреть файл

@ -0,0 +1,232 @@
##
# 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
@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
elsif @text.sub!(/^\s*:?call-seq:(.*?)(^\s*$|\z)/m, '') then
seq = $1
seq.gsub!(/^\s*/, '')
method.call_seq = seq
end
#elsif @text.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') then
# method.call_seq = $1.strip
#end
method
end
##
# A comment is empty if its text String is empty.
def empty?
@text.empty?
end
##
# HACK dubious
def force_encoding encoding
@text.force_encoding encoding
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.absolute_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.force_encoding @text.encoding if Object.const_defined? :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
end
##
# Returns true if this comment is in TomDoc format.
def tomdoc?
@format == 'tomdoc'
end
end

Просмотреть файл

@ -1,16 +1,14 @@
require 'rdoc/code_object'
##
# A constant
class RDoc::Constant < RDoc::CodeObject
##
# If this constant is an alias for a module or class,
# this is the RDoc::ClassModule it is an alias for.
# +nil+ otherwise.
MARSHAL_VERSION = 0 # :nodoc:
attr_accessor :is_alias_for
##
# Sets the module or class this is constant is an alias for.
attr_writer :is_alias_for
##
# The constant's name
@ -22,14 +20,23 @@ class RDoc::Constant < RDoc::CodeObject
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
@name = name
@value = value
@is_alias_for = nil
@visibility = nil
self.comment = comment
end
@ -59,6 +66,27 @@ class RDoc::Constant < RDoc::CodeObject
super or is_alias_for && 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,
@ -67,12 +95,76 @@ class RDoc::Constant < RDoc::CodeObject
end
##
# Path to this constant
# 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.absolute_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]
@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

Просмотреть файл

@ -1,4 +1,4 @@
require 'rdoc/code_object'
require 'cgi'
##
# A Context is something that can hold modules, classes, methods, attributes,
@ -14,6 +14,12 @@ class RDoc::Context < RDoc::CodeObject
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
@ -24,6 +30,11 @@ class RDoc::Context < RDoc::CodeObject
attr_reader :attributes
##
# Block params to be used in the next MethodAttr parsed under this context
attr_accessor :block_params
##
# Constants defined
@ -44,6 +55,11 @@ class RDoc::Context < RDoc::CodeObject
attr_reader :includes
##
# Modules this context is extended with
attr_reader :extends
##
# Methods defined in this context
@ -72,7 +88,7 @@ class RDoc::Context < RDoc::CodeObject
attr_accessor :unmatched_alias_lists
##
# Aliases that could not eventually be resolved.
# Aliases that could not be resolved.
attr_reader :external_aliases
@ -87,123 +103,16 @@ class RDoc::Context < RDoc::CodeObject
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
##
# 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 Section
include RDoc::Text
##
# Section comment
attr_reader :comment
##
# 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.succ!
@sequence = @@sequence.dup
@comment = extract_comment comment
end
##
# Sections are equal when they have the same #title
def == other
self.class === other and @title == other.title
end
##
# Anchor reference for linking to this section
def aref
title = @title || '[untitled]'
CGI.escape(title).gsub('%', '-').sub(/^-/, '')
end
##
# Appends +comment+ to the current comment separated by a rule.
def comment= comment
comment = extract_comment comment
return if comment.empty?
if @comment then
@comment += "\n# ---\n#{comment}"
else
@comment = comment
end
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
if comment =~ /^#[ \t]*:section:.*\n/ then
start = $`
rest = $'
if start.empty? then
rest
else
rest.sub(/#{start.chomp}\Z/, '')
end
else
comment
end
end
def inspect # :nodoc:
"#<%s:0x%x %p>" % [self.class, object_id, title]
end
##
# Section sequence number (deprecated)
def sequence
warn "RDoc::Context::Section#sequence is deprecated, use #aref"
@sequence
end
end
##
# Creates an unnamed empty context with public current visibility
@ -235,6 +144,7 @@ class RDoc::Context < RDoc::CodeObject
@aliases = []
@requires = []
@includes = []
@extends = []
@constants = []
@external_aliases = []
@ -242,8 +152,12 @@ class RDoc::Context < RDoc::CodeObject
# a method not yet encountered).
@unmatched_alias_lists = {}
@methods_hash = {}
@methods_hash = {}
@constants_hash = {}
@params = nil
@store ||= nil
end
##
@ -366,12 +280,12 @@ class RDoc::Context < RDoc::CodeObject
if full_name =~ /^(.+)::(\w+)$/ then
name = $2
ename = $1
enclosing = RDoc::TopLevel.classes_hash[ename] ||
RDoc::TopLevel.modules_hash[ename]
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 = RDoc::TopLevel.classes_hash[given_name] || RDoc::TopLevel.modules_hash[given_name]
enclosing = @store.classes_hash[given_name] ||
@store.modules_hash[given_name]
return enclosing if enclosing
# not found: create the parent(s)
names = ename.split('::')
@ -410,7 +324,7 @@ class RDoc::Context < RDoc::CodeObject
end
# did we believe it was a module?
mod = RDoc::TopLevel.modules_hash.delete superclass
mod = @store.modules_hash.delete superclass
upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
@ -418,7 +332,7 @@ class RDoc::Context < RDoc::CodeObject
superclass = nil if superclass == full_name
end
klass = RDoc::TopLevel.classes_hash[full_name]
klass = @store.classes_hash[full_name]
if klass then
# if TopLevel, it may not be registered in the classes:
@ -435,7 +349,7 @@ class RDoc::Context < RDoc::CodeObject
end
else
# this is a new class
mod = RDoc::TopLevel.modules_hash.delete full_name
mod = @store.modules_hash.delete full_name
if mod then
klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
@ -445,10 +359,12 @@ class RDoc::Context < RDoc::CodeObject
klass = class_type.new name, superclass
enclosing.add_class_or_module(klass, enclosing.classes_hash,
RDoc::TopLevel.classes_hash)
@store.classes_hash)
end
end
klass.parent = self
klass
end
@ -463,6 +379,7 @@ class RDoc::Context < RDoc::CodeObject
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
@ -504,12 +421,20 @@ class RDoc::Context < RDoc::CodeObject
# Adds included module +include+ which should be an RDoc::Include
def add_include include
add_to @includes, include unless
@includes.map { |i| i.full_name }.include? include.full_name
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.
@ -523,6 +448,10 @@ class RDoc::Context < RDoc::CodeObject
if known then
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}"
else
@methods_hash[key] = method
method.visibility = @visibility
@ -542,9 +471,9 @@ class RDoc::Context < RDoc::CodeObject
return mod if mod
full_name = child_name name
mod = RDoc::TopLevel.modules_hash[full_name] || class_type.new(name)
mod = @store.modules_hash[full_name] || class_type.new(name)
add_class_or_module(mod, @modules, RDoc::TopLevel.modules_hash)
add_class_or_module mod, @modules, @store.modules_hash
end
##
@ -554,31 +483,34 @@ class RDoc::Context < RDoc::CodeObject
def add_module_alias from, name, file
return from if @done_documenting
to_name = child_name(name)
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 as a class when we find
# where we already know BasicObject is a class when we find
# BasicObject = BlankSlate
return from if RDoc::TopLevel.find_class_or_module(to_name)
return from if @store.find_class_or_module to_name
if from.module? then
RDoc::TopLevel.modules_hash[to_name] = from
@modules[name] = from
to = from.dup
to.name = name
to.full_name = nil
if to.module? then
@store.modules_hash[to_name] = to
@modules[name] = to
else
RDoc::TopLevel.classes_hash[to_name] = from
@classes[name] = from
@store.classes_hash[to_name] = to
@classes[name] = to
end
# HACK: register a constant for this alias:
# constant value and comment will be updated after,
# when the Ruby parser adds the constant
const = RDoc::Constant.new name, nil, ''
# 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
from
to
end
##
@ -602,9 +534,9 @@ class RDoc::Context < RDoc::CodeObject
#
# See also RDoc::Context::Section
def add_section title, comment
def add_section title, comment = nil
if section = @sections[title] then
section.comment = comment
section.add_comment comment if comment
else
section = Section.new self, title, comment
@sections[title] = section
@ -616,9 +548,11 @@ class RDoc::Context < RDoc::CodeObject
##
# Adds +thing+ to the collection +array+
def add_to(array, thing)
def add_to array, thing
array << thing if @document_self
thing.parent = self
thing.parent = self
thing.store = @store if @store
thing.section = current_section
end
@ -628,7 +562,7 @@ class RDoc::Context < RDoc::CodeObject
# This means any of: comment, aliases, methods, attributes, external
# aliases, require, constant.
#
# Includes are also checked unless <tt>includes == false</tt>.
# Includes and extends are also checked unless <tt>includes == false</tt>.
def any_content(includes = true)
@any_content ||= !(
@ -640,7 +574,7 @@ class RDoc::Context < RDoc::CodeObject
@requires.empty? &&
@constants.empty?
)
@any_content || (includes && !@includes.empty?)
@any_content || (includes && !(@includes + @extends).empty? )
end
##
@ -723,6 +657,9 @@ class RDoc::Context < RDoc::CodeObject
##
# 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
@ -755,11 +692,20 @@ class RDoc::Context < RDoc::CodeObject
@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
@method_list.sort.each {|m| yield m}
return enum_for __method__ unless block_given?
@method_list.sort.each { |m| yield m }
end
##
@ -773,13 +719,15 @@ class RDoc::Context < RDoc::CodeObject
# NOTE: Do not edit collections yielded by this method
def each_section # :yields: section, constants, attributes
constants = @constants.group_by do |constant| constant.section end
constants.default = []
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 = []
@sections.sort_by { |title, _| title.to_s }.each do |_, section|
sort_sections.each do |section|
yield section, constants[section].sort, attributes[section].sort
end
end
@ -851,8 +799,8 @@ class RDoc::Context < RDoc::CodeObject
##
# Finds a file with +name+ in this context
def find_file_named(name)
top_level.class.find_file_named(name)
def find_file_named name
@store.find_file_named name
end
##
@ -922,21 +870,21 @@ class RDoc::Context < RDoc::CodeObject
# look for a class or module 'symbol'
case symbol
when /^::/ then
result = RDoc::TopLevel.find_class_or_module(symbol)
result = @store.find_class_or_module symbol
when /^(\w+):+(.+)$/
suffix = $2
top = $1
searched = self
loop do
while searched do
mod = searched.find_module_named(top)
break unless mod
result = RDoc::TopLevel.find_class_or_module(mod.full_name + '::' + suffix)
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
loop do
while searched do
result = searched.find_module_named(symbol)
break if result || searched.is_a?(RDoc::TopLevel)
searched = searched.parent
@ -985,6 +933,8 @@ class RDoc::Context < RDoc::CodeObject
##
# Instance methods
#--
# TODO rename to instance_methods
def instance_method_list
@instance_method_list ||= method_list.reject { |a| a.singleton }
@ -1098,24 +1048,23 @@ class RDoc::Context < RDoc::CodeObject
##
# Only called when min_visibility == :public or :private
def remove_invisible_in(array, min_visibility) # :nodoc:
if min_visibility == :public
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
e.visibility == :private and not e.force_documentation
}
end
end
##
# Tries to resolve unmatched aliases when a method
# or attribute has just been added.
# Tries to resolve unmatched aliases when a method or attribute has just
# been added.
def resolve_aliases(added)
def resolve_aliases added
# resolve any pending unmatched aliases
key = added.pretty_name
unmatched_alias_list = @unmatched_alias_lists[key]
@ -1127,6 +1076,31 @@ class RDoc::Context < RDoc::CodeObject
@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
@ -1155,6 +1129,26 @@ class RDoc::Context < RDoc::CodeObject
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
@ -1179,13 +1173,16 @@ class RDoc::Context < RDoc::CodeObject
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
RDoc::TopLevel.classes_hash[mod.full_name] = klass
enclosing.classes_hash[mod.name] = klass
@store.classes_hash[mod.full_name] = klass
enclosing.classes_hash[mod.name] = klass
klass
end
autoload :Section, 'rdoc/context/section'
end

238
lib/rdoc/context/section.rb Normal file
Просмотреть файл

@ -0,0 +1,238 @@
##
# 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.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
##
# 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
##
# 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

Просмотреть файл

@ -18,7 +18,7 @@ class RDoc::CrossReference
#
# See CLASS_REGEXP_STR
METHOD_REGEXP_STR = '([a-z]\w*[!?=]?)(?:\([\w.+*/=<>-]*\))?'
METHOD_REGEXP_STR = '([a-z]\w*[!?=]?|%)(?:\([\w.+*/=<>-]*\))?'
##
# Regular expressions matching text that should potentially have
@ -27,63 +27,79 @@ class RDoc::CrossReference
# have been suppressed, since the suppression characters are removed by the
# code that is triggered.
CROSSREF_REGEXP = /(
# A::B::C.meth
#{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
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 a #)
| \\?\##{METHOD_REGEXP_STR}
# Stand-alone method (preceded by ::)
| ::#{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)
# 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 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<]
)/x
# 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 = /(
# A::B::C.meth
#{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
ALL_CROSSREF_REGEXP = /
(?:^|\s)
(
(?:
# A::B::C.meth
#{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
# Stand-alone method
| \\?#{METHOD_REGEXP_STR}
# Stand-alone method
| \\?#{METHOD_REGEXP_STR}
# A::B::C
| #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z)
# A::B::C
| #{CLASS_REGEXP_STR}(?=[@\s).?!,;<\000]|\z)
# Things that look like filenames
| (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+
# Things that look like filenames
| (?:\.\.\/)*[-\/\w]+[_\/.][-\w\/.]+
# Things that have markup suppressed
| \\[^\s<]
)/x
# 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
@ -93,6 +109,7 @@ class RDoc::CrossReference
def initialize context
@context = context
@store = context.store
@seen = {}
end
@ -107,16 +124,6 @@ class RDoc::CrossReference
def resolve name, text
return @seen[name] if @seen.include? name
# Find class, module, or method in class or module.
#
# Do not, however, use an if/elsif/else chain to do so. Instead, test
# each possible pattern until one matches. The reason for this is that a
# string like "YAML.txt" could be the txt() class method of class YAML (in
# which case it would match the first pattern, which splits the string
# into container and method components and looks up both) or a filename
# (in which case it would match the last pattern, which just checks
# whether the string as a whole is a known symbol).
if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then
type = $2
type = '' if type == '.' # will find either #method or ::method
@ -141,12 +148,15 @@ class RDoc::CrossReference
ref = case name
when /^\\(#{CLASS_REGEXP_STR})$/o then
ref = @context.find_symbol $1
@context.find_symbol $1
else
ref = @context.find_symbol name
@context.find_symbol name
end unless ref
ref = nil if RDoc::Alias === ref # external alias: can't link to it
# 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

Просмотреть файл

@ -1,7 +1,5 @@
# coding: US-ASCII
require 'rdoc'
##
# This class is a wrapper around File IO and Encoding that helps RDoc load
# files and convert them to the correct encoding.
@ -27,26 +25,40 @@ module RDoc::Encoding
RDoc::Encoding.set_encoding content
if Object.const_defined? :Encoding then
encoding ||= Encoding.default_external
orig_encoding = content.encoding
begin
encoding ||= Encoding.default_external
orig_encoding = content.encoding
if utf8 then
content.force_encoding Encoding::UTF_8
content.encode! encoding
else
# assume the content is in our output encoding
content.force_encoding encoding
end
if utf8 then
content.force_encoding Encoding::UTF_8
content.encode! encoding
else
# assume the content is in our output encoding
content.force_encoding encoding
end
unless content.valid_encoding? then
# revert and try to transcode
content.force_encoding orig_encoding
content.encode! encoding
end
unless content.valid_encoding? then
# revert and try to transcode
content.force_encoding orig_encoding
content.encode! encoding
end
unless content.valid_encoding? then
warn "unable to convert #{filename} to #{encoding}, skipping"
content = nil
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.force_encoding orig_encoding
content.encode!(encoding,
:invalid => :replace, :undef => :replace,
:replace => '?')
return content
else
warn "unable to convert #{e.message} for #{filename}, skipping"
return nil
end
end
end
@ -55,15 +67,6 @@ module RDoc::Encoding
raise unless e.message =~ /unknown encoding name - (.*)/
warn "unknown encoding name \"#{$1}\" for #{filename}, skipping"
nil
rescue Encoding::UndefinedConversionError => e
if force_transcode then
content.force_encoding orig_encoding
content.encode! encoding, :undef => :replace, :replace => '?'
content
else
warn "unable to convert #{e.message} for #{filename}, skipping"
nil
end
rescue Errno::EISDIR, Errno::ENOENT
nil
end

18
lib/rdoc/erb_partial.rb Normal file
Просмотреть файл

@ -0,0 +1,18 @@
##
# 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

117
lib/rdoc/extend.rb Normal file
Просмотреть файл

@ -0,0 +1,117 @@
##
# A Module extension in a class with \#extend
class RDoc::Extend < RDoc::CodeObject
##
# Name of extension module
attr_accessor :name
##
# Creates a new Extend for +name+ with +comment+
def initialize(name, comment)
super()
@name = name
self.comment = comment
@module = nil # cache for module if found
end
##
# Extends 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? ==
##
# 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.extend %s>" % [
self.class,
object_id,
parent_name, @name,
]
end
##
# Attempts to locate the extend module object. Returns the name if not
# known.
#
# The scoping rules of Ruby to resolve the name of an extension module are:
# - first look into the children of the current context;
# - if not found, look into the children of extension modules,
# in reverse extend order;
# - if still not found, go up the hierarchy of names.
#
# This method has <code>O(n!)</code> behavior when the module calling
# extend is referencing nonexistent modules. Avoid calling #module until
# after all the files are parsed. This behavior is due to ruby's constant
# lookup behavior.
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.extends.take_while { |i| i != self }.reverse
searched.each do |i|
ext = i.module
next if String === ext
full_name = ext.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:
"extend #@name in: #{parent}"
end
end

Просмотреть файл

@ -1,12 +1,10 @@
require 'rdoc'
##
# 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
# == Registering a Generator
#
# Generators are registered by calling RDoc::RDoc.add_generator with the class
# of the generator:
@ -15,26 +13,38 @@ require 'rdoc'
# RDoc::RDoc.add_generator self
# end
#
# = Adding Options to +rdoc+
# == 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 OptionParser for details on how to add options.
# 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
# == Generator Instantiation
#
# After parsing, RDoc::RDoc will instantiate a generator by calling
# #initialize with an RDoc::Options instance.
# #initialize with an RDoc::Store instance and an RDoc::Options instance.
#
# RDoc will then call #generate on the generator instance and pass in an Array
# of RDoc::TopLevel instances, each representing a parsed file. You can use
# the various class methods on RDoc::TopLevel and in the RDoc::CodeObject tree
# to create your desired output format.
# 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'
end

Просмотреть файл

@ -1,9 +1,8 @@
# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
require 'pathname'
require 'erb'
require 'fileutils'
require 'rdoc/erbio'
require 'pathname'
require 'rdoc/generator/markup'
##
@ -46,6 +45,11 @@ require 'rdoc/generator/markup'
# 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
@ -53,6 +57,7 @@ class RDoc::Generator::Darkfish
include ERB::Util
##
# Path to this file's parent directory. Used to find templates and other
# resources.
@ -61,7 +66,7 @@ class RDoc::Generator::Darkfish
##
# Release Version
VERSION = '2'
VERSION = '3'
##
# Description of this generator
@ -69,25 +74,87 @@ class RDoc::Generator::Darkfish
DESCRIPTION = 'HTML generator, written by Michael Granger'
##
# Initialize a few instance variables before we start
# The relative path to style sheets and javascript. By default this is set
# the same as the rel_prefix.
def initialize options
@options = options
attr_accessor :asset_rel_path
@template_dir = Pathname.new options.template_dir
@template_cache = {}
##
# The path to generate files into, combined with <tt>--op</tt> from the
# options for a full path.
@files = nil
@classes = nil
attr_reader :base_dir
@basedir = Pathname.pwd.expand_path
end
##
# 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 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
@ -126,7 +193,7 @@ class RDoc::Generator::Darkfish
def write_style_sheet
debug_msg "Copying static files"
options = { :verbose => $DEBUG_RDOC, :noop => @options.dry_run }
options = { :verbose => $DEBUG_RDOC, :noop => @dry_run }
FileUtils.cp @template_dir + 'rdoc.css', '.', options
@ -134,7 +201,7 @@ class RDoc::Generator::Darkfish
next if File.directory? path
next if File.basename(path) =~ /^\./
dst = Pathname.new(path).relative_path_from @template_dir
dst = Pathname.new(path).relative_path_from @template_dir
# I suck at glob
dst_dir = dst.dirname
@ -148,19 +215,17 @@ class RDoc::Generator::Darkfish
# Build the initial indices and output objects based on an array of TopLevel
# objects containing the extracted information.
def generate top_levels
@outputdir = Pathname.new(@options.op_dir).expand_path(@basedir)
def generate
setup
@files = top_levels.sort
@classes = RDoc::TopLevel.all_classes_and_modules.sort
@methods = @classes.map { |m| m.method_list }.flatten.sort
@modsort = get_sorted_module_list(@classes)
# Now actually write the output
write_style_sheet
generate_index
generate_class_files
generate_file_files
generate_table_of_contents
@json_index.generate
copy_static
rescue => e
debug_msg "%s: %s\n %s" % [
@ -170,42 +235,64 @@ class RDoc::Generator::Darkfish
raise
end
protected
##
# 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)
nscounts = classes.inject({}) do |counthash, klass|
top_level = klass.full_name.gsub(/::.*/, '')
counthash[top_level] ||= 0
counthash[top_level] += 1
counthash
end
# Sort based on how often the top level namespace occurs, and then on the
# name of the module -- this works for projects that put their stuff into
# a namespace, of course, but doesn't hurt if they don't.
classes.sort_by do |klass|
top_level = klass.full_name.gsub( /::.*/, '' )
[nscounts[top_level] * -1, klass.full_name]
end.select do |klass|
def get_sorted_module_list classes
classes.select do |klass|
klass.display?
end
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 = @basedir + @options.op_dir + 'index.html'
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
# suppress 1.9.3 warning
asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
@title = @options.title
render_template template_file, out_file do |io| binding end
rescue => e
@ -217,10 +304,40 @@ class RDoc::Generator::Darkfish
end
##
# Generate a documentation file for each class
# 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
# suppress 1.9.3 warning
asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
svninfo = 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| binding end
end
##
# Generate a documentation file for each class and module
def generate_class_files
template_file = @template_dir + 'classpage.rhtml'
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}"
@ -228,14 +345,8 @@ class RDoc::Generator::Darkfish
@classes.each do |klass|
current = klass
debug_msg " working on %s (%s)" % [klass.full_name, klass.path]
out_file = @outputdir + klass.path
# suppress 1.9.3 warning
rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
svninfo = svninfo = self.get_svninfo(klass)
debug_msg " rendering #{out_file}"
render_template template_file, out_file do |io| binding end
generate_class klass, template_file
end
rescue => e
error = RDoc::Error.new \
@ -249,19 +360,56 @@ class RDoc::Generator::Darkfish
# Generate a documentation file for each file
def generate_file_files
template_file = @template_dir + 'filepage.rhtml'
return unless template_file.exist?
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|
out_file = @outputdir + file.path
debug_msg " working on %s (%s)" % [file.full_name, out_file]
# suppress 1.9.3 warning
rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
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
# suppress 1.9.3 warning
asset_rel_prefix = 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
debug_msg " rendering #{out_file}"
render_template template_file, out_file do |io| binding end
end
rescue => e
@ -272,6 +420,134 @@ class RDoc::Generator::Darkfish
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
# suppress 1.9.3 warning
current = current = file
asset_rel_prefix = 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| binding end
end
##
# Generates the 404 page for the RDoc servlet
def generate_servlet_not_found path
setup
template_file = @template_dir + 'servlet_not_found.rhtml'
return unless template_file.exist?
debug_msg "Rendering the servlet root page..."
rel_prefix = rel_prefix = ''
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
# suppress 1.9.3 warning
asset_rel_prefix = asset_rel_prefix = ''
@title = 'Not Found'
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
##
# 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 = rel_prefix = ''
search_index_rel_prefix = rel_prefix
search_index_rel_prefix += @asset_rel_path if @file_output
# suppress 1.9.3 warning
asset_rel_prefix = asset_rel_prefix = ''
@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
# suppress 1.9.3 warning
asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path
@title = "Table of Contents - #{@options.title}"
render_template template_file, out_file do |io| binding 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
##
# 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.
@ -324,6 +600,46 @@ class RDoc::Generator::Darkfish
}
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+.
@ -332,28 +648,33 @@ class RDoc::Generator::Darkfish
#
# An io will be yielded which must be captured by binding in the caller.
def render_template template_file, out_file # :yield: io
template = template_for template_file
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
unless @options.dry_run then
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 if Object.const_defined? :Encoding
context = yield io
@context = yield io
template_result template, context, template_file
template_result template, @context, template_file
end
else
context = yield nil
@context = yield nil
output = template_result template, context, template_file
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
@ -374,14 +695,25 @@ class RDoc::Generator::Darkfish
##
# Retrieves a cache template for +file+, if present, or fills the cache.
def template_for file
def template_for file, page = true, klass = ERB
template = @template_cache[file]
return template if template
klass = @options.dry_run ? ERB : RDoc::ERBIO
template = if page then
assemble_template file
else
file.read
end
template = klass.new file.read, nil, '<>'
erbout = if page then
'io'
else
file_var = File.basename(file).sub(/\..*/, '')
"_erbout_#{file_var}"
end
template = klass.new template, nil, '<>', erbout
@template_cache[file] = template
template
end

Просмотреть файл

@ -0,0 +1,248 @@
require 'json'
##
# 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 if Object.const_defined? :Encoding
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
##
# 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

Просмотреть файл

@ -1,14 +1,8 @@
# This file is loaded by generators. It allows RDoc's CodeObject tree to
# avoid loading generator code to increase startup time (for ri).
require 'rdoc/text'
require 'rdoc/code_objects'
require 'rdoc/generator'
require 'rdoc/markup/to_html_crossref'
require 'rdoc/ruby_token'
##
# 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
@ -39,18 +33,18 @@ module RDoc::Generator::Markup
def formatter
return @formatter if defined? @formatter
show_hash = RDoc::RDoc.current.options.show_hash
hyperlink_all = RDoc::RDoc.current.options.hyperlink_all
options = @store.rdoc.options
this = RDoc::Context === self ? self : @parent
@formatter = RDoc::Markup::ToHtmlCrossref.new(this.path, this, show_hash,
hyperlink_all)
@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
# sprintf'd into +url+ instead.
# will replace the %s using sprintf on the +url+.
def cvs_url(url, full_path)
if /%s/ =~ url then
@ -62,10 +56,14 @@ module RDoc::Generator::Markup
end
class RDoc::AnyMethod
class RDoc::CodeObject
include RDoc::Generator::Markup
end
class RDoc::MethodAttr
@add_line_numbers = false
class << self
@ -82,7 +80,8 @@ class RDoc::AnyMethod
#
# # File xxxxx, line dddd
#
# If it has, line numbers are added an ', line dddd' is removed.
# 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')
@ -111,32 +110,7 @@ class RDoc::AnyMethod
def markup_code
return '' unless @token_stream
src = ""
@token_stream.each do |t|
next unless t
style = case t
when RDoc::RubyToken::TkCONSTANT then 'ruby-constant'
when RDoc::RubyToken::TkKW then 'ruby-keyword'
when RDoc::RubyToken::TkIVAR then 'ruby-ivar'
when RDoc::RubyToken::TkOp then 'ruby-operator'
when RDoc::RubyToken::TkId then 'ruby-identifier'
when RDoc::RubyToken::TkNode then 'ruby-node'
when RDoc::RubyToken::TkCOMMENT then 'ruby-comment'
when RDoc::RubyToken::TkREGEXP then 'ruby-regexp'
when RDoc::RubyToken::TkSTRING then 'ruby-string'
when RDoc::RubyToken::TkVal then 'ruby-value'
end
text = CGI.escapeHTML t.text
if style then
src << "<span class=\"#{style}\">#{text}</span>"
else
src << text
end
end
src = RDoc::TokenStream.to_html @token_stream
# dedent the source
indent = src.length
@ -151,34 +125,21 @@ class RDoc::AnyMethod
end
src.gsub!(/^#{' ' * indent}/, '') if indent > 0
add_line_numbers(src) if self.class.add_line_numbers
add_line_numbers(src) if RDoc::MethodAttr.add_line_numbers
src
end
end
class RDoc::Attr
class RDoc::ClassModule
include RDoc::Generator::Markup
##
# Handy wrapper for marking up this class or module's comment
end
class RDoc::Alias
include RDoc::Generator::Markup
end
class RDoc::Constant
include RDoc::Generator::Markup
end
class RDoc::Context
include RDoc::Generator::Markup
def description
markup @comment_location
end
end
@ -195,7 +156,7 @@ class RDoc::TopLevel
# command line option to set.
def cvs_url
url = RDoc::RDoc.current.options.webcvs
url = @store.rdoc.options.webcvs
if /%s/ =~ url then
url % @absolute_name

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/generator'
require 'rdoc/ri'
##
# Generates ri data files
@ -16,70 +13,17 @@ class RDoc::Generator::RI
##
# Set up a new ri generator
def initialize options #:not-new:
@options = options
@old_siginfo = nil
@current = nil
@store = RDoc::RI::Store.new '.'
@store.dry_run = @options.dry_run
@store.encoding = @options.encoding if @options.respond_to? :encoding
def initialize store, options #:not-new:
@options = options
@store = store
@store.path = '.'
end
##
# Build the initial indices and output objects based on an array of TopLevel
# objects containing the extracted information.
# Writes the parsed data store to disk for use by ri.
def generate top_levels
install_siginfo_handler
@store.load_cache
RDoc::TopLevel.all_classes_and_modules.each do |klass|
@current = "#{klass.class}: #{klass.full_name}"
@store.save_class klass
klass.each_method do |method|
@current = "#{method.class}: #{method.full_name}"
@store.save_method klass, method
end
klass.each_attribute do |attribute|
@store.save_method klass, attribute
end
end
@current = 'saving cache'
@store.save_cache
ensure
@current = nil
remove_siginfo_handler
end
##
# Installs a siginfo handler that prints the current filename.
def install_siginfo_handler
return unless Signal.list.key? 'INFO'
@old_siginfo = trap 'INFO' do
puts @current if @current
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
def generate
@store.save
end
end

Просмотреть файл

@ -0,0 +1,5 @@
<footer id="validator-badges">
<p><a href="http://validator.w3.org/check/referer">[Validate]</a>
<p>Generated by <a href="https://github.com/rdoc/rdoc">RDoc</a> <%= RDoc::VERSION %>.
<p>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %>.
</footer>

Просмотреть файл

@ -0,0 +1,16 @@
<meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type">
<title><%= h @title %></title>
<link type="text/css" media="screen" href="<%= asset_rel_prefix %>/rdoc.css" rel="stylesheet">
<script type="text/javascript">
var rdoc_rel_prefix = "<%= rel_prefix %>/";
</script>
<script type="text/javascript" charset="utf-8" src="<%= asset_rel_prefix %>/js/jquery.js"></script>
<script type="text/javascript" charset="utf-8" src="<%= asset_rel_prefix %>/js/navigation.js"></script>
<script type="text/javascript" charset="utf-8" src="<%= search_index_rel_prefix %>/js/search_index.js"></script>
<script type="text/javascript" charset="utf-8" src="<%= asset_rel_prefix %>/js/search.js"></script>
<script type="text/javascript" charset="utf-8" src="<%= asset_rel_prefix %>/js/searcher.js"></script>
<script type="text/javascript" charset="utf-8" src="<%= asset_rel_prefix %>/js/darkfish.js"></script>

Просмотреть файл

@ -0,0 +1,18 @@
<% if !svninfo.empty? then %>
<nav id="file-svninfo-section" class="section">
<h3 class="section-header">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>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,9 @@
<nav id="classindex-section" class="section project-section">
<h3 class="section-header">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>
</nav>

Просмотреть файл

@ -0,0 +1,16 @@
<% unless klass.extends.empty? then %>
<!-- Extension Modules -->
<nav id="extends-section" class="section">
<h3 class="section-header">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>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,8 @@
<nav id="file-list-section" class="section">
<h3 class="section-header">Defined In</h3>
<ul>
<% klass.in_files.each do |tl| %>
<li><%= h tl.absolute_name %>
<% end %>
</ul>
</nav>

Просмотреть файл

@ -0,0 +1,16 @@
<% unless klass.includes.empty? then %>
<!-- Included Modules -->
<nav id="includes-section" class="section">
<h3 class="section-header">Included Modules</h3>
<ul class="link-list">
<% klass.each_include do |inc| %>
<% unless String === inc.module 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>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,14 @@
<nav id="home-section" class="section">
<h3 class="section-header">Documentation</h3>
<ul>
<% installed.each do |name, href, exists| %>
<li class="folder">
<% if exists then %>
<a href="<%= href %>"><%= h name %></a>
<% else %>
<%= h name %>
<% end %>
<% end %>
</ul>
</nav>

Просмотреть файл

@ -0,0 +1,12 @@
<% unless klass.method_list.empty? then %>
<!-- Method Quickref -->
<nav id="method-list-section" class="section">
<h3 class="section-header">Methods</h3>
<ul class="link-list">
<% 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>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,7 @@
<nav id="home-section" class="section">
<h3 class="section-header">
<a href="<%= rel_prefix %>/index.html">Home</a>
<a href="<%= rel_prefix %>/table_of_contents.html#classes">Classes</a>
<a href="<%= rel_prefix %>/table_of_contents.html#methods">Methods</a>
</h3>
</nav>

Просмотреть файл

@ -0,0 +1,12 @@
<% simple_files = @files.select { |f| f.text? } %>
<% unless simple_files.empty? then %>
<nav id="fileindex-section" class="section project-section">
<h3 class="section-header">Pages</h3>
<ul>
<% simple_files.each do |f| %>
<li class="file"><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.page_name %></a>
<% end %>
</ul>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,10 @@
<% if klass.type == 'class' then %>
<nav id="parent-class-section" class="section">
<h3 class="section-header">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 %>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,10 @@
<nav id="search-section" class="section project-section" class="initially-hidden">
<form action="#" method="get" accept-charset="utf-8">
<h3 class="section-header">
<input type="text" name="search" placeholder="Search" id="search-field"
title="Type to search, Up and Down to navigate, Enter to load">
</h3>
</form>
<ul id="search-results" class="initially-hidden"></ul>
</nav>

Просмотреть файл

@ -0,0 +1,10 @@
<% unless klass.sections.length == 1 then %>
<nav id="sections-section" class="section">
<h3 class="section-header">Sections</h3>
<ul class="link-list">
<% klass.sort_sections.each do |section| %>
<li><a href="#<%= section.aref %>"><%= h section.title %></a></li>
<% end %>
</ul>
</nav>
<% end %>

Просмотреть файл

@ -0,0 +1,13 @@
<% table = current.parse(current.comment).table_of_contents
if table.length > 1 then %>
<div id="table-of-contents">
<nav class="section">
<h3 class="section-header">Table of Contents</h3>
<ul>
<% table.each do |heading| %>
<li><a href="#<%= heading.aref %>"><%= heading.plain_html %></a>
<% end %>
</ul>
</nav>
</div>
<% end %>

Просмотреть файл

@ -0,0 +1,179 @@
<body id="top" class="<%= klass.type %>">
<nav id="metadata">
<%= render '_sidebar_navigation.rhtml' %>
<%= render '_sidebar_search.rhtml' %>
<%= render '_sidebar_table_of_contents.rhtml' %>
<div id="file-metadata">
<%= render '_sidebar_in_files.rhtml' %>
<%= render '_sidebar_VCS_info.rhtml' %>
</div>
<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>
<div id="project-metadata">
<%= render '_sidebar_pages.rhtml' %>
<%= render '_sidebar_classes.rhtml' %>
</div>
</nav>
<div id="documentation">
<h1 class="<%= klass.type %>"><%= klass.type %> <%= klass.full_name %></h1>
<div id="description" class="description">
<%= klass.description %>
</div><!-- description -->
<% klass.each_section do |section, constants, attributes| %>
<% constants = constants.select { |const| const.display? } %>
<% attributes = attributes.select { |attr| attr.display? } %>
<section id="<%= section.aref %>" class="documentation-section">
<% if section.title then %>
<div class="documentation-section-title">
<h2 class="section-header">
<%= section.title %>
</h2>
<span class="section-click-top">
<a href="#top">&uarr; top</a>
</span>
</div>
<% end %>
<% if section.comment then %>
<div class="description">
<%= section.description %>
</div>
<% end %>
<% unless constants.empty? then %>
<!-- Constants -->
<section id="constants-list" class="section">
<h3 class="section-header">Constants</h3>
<dl>
<% constants.each do |const| %>
<dt id="<%= const.name %>"><%= const.name %>
<% if const.comment then %>
<dd class="description"><%= const.description.strip %>
<% else %>
<dd class="description missing-docs">(Not documented)
<% end %>
<% end %>
</dl>
</section>
<% end %>
<% unless attributes.empty? then %>
<!-- Attributes -->
<section id="attribute-method-details" class="method-section section">
<h3 class="section-header">Attributes</h3>
<% 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><!-- attribute-method-details -->
<% end %>
<!-- Methods -->
<% 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 section">
<h3 class="section-header"><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
<% 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"><%= 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><!-- <%= method.html_name %>-source -->
<% 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><!-- <%= method.html_name %>-method -->
<% end %>
</section><!-- <%= visibility %>-<%= type %>-method-details -->
<% end
end %>
</section><!-- <%= section.aref %> -->
<% end %>
</div><!-- documentation -->

Просмотреть файл

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

Просмотреть файл

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

Двоичные данные
lib/rdoc/generator/template/darkfish/images/add.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 733 B

Двоичные данные
lib/rdoc/generator/template/darkfish/images/arrow_up.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 372 B

Двоичные данные
lib/rdoc/generator/template/darkfish/images/delete.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 715 B

Двоичные данные
lib/rdoc/generator/template/darkfish/images/tag_blue.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичные данные
lib/rdoc/generator/template/darkfish/images/transparent.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 97 B

Просмотреть файл

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

Просмотреть файл

@ -9,12 +9,12 @@
/* Provide console simulation for firebug-less environments */
if (!("console" in window) || !("firebug" in console)) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
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() {};
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
};
@ -23,94 +23,131 @@ if (!("console" in window) || !("firebug" in console)) {
*/
$.fn.unwrap = function( expr ) {
return this.each( function() {
$(this).parents( expr ).eq( 0 ).after( this ).remove();
$(this).parents( expr ).eq( 0 ).after( this ).remove();
});
};
function showSource( e ) {
var target = e.target;
var codeSections = $(target).
parents('.method-detail').
find('.method-source-code');
var target = e.target;
var codeSections = $(target).
parents('.method-detail').
find('.method-source-code');
$(target).
parents('.method-detail').
find('.method-source-code').
slideToggle();
$(target).
parents('.method-detail').
find('.method-source-code').
slideToggle();
};
function hookSourceViews() {
$('.method-description,.method-heading').click( showSource );
$('.method-heading').click( showSource );
};
function toggleDebuggingSection() {
$('.debugging-section').slideToggle();
$('.debugging-section').slideToggle();
};
function hookDebuggingToggle() {
$('#debugging-toggle img').click( toggleDebuggingSection );
$('#debugging-toggle img').click( toggleDebuggingSection );
};
function hookQuickSearch() {
$('.quicksearch-field').each( function() {
var searchElems = $(this).parents('.section').find( 'li' );
var toggle = $(this).parents('.section').find('h3 .search-toggle');
// console.debug( "Toggle is: %o", toggle );
var qsbox = $(this).parents('form').get( 0 );
function hookTableOfContentsToggle() {
$('.indexpage li .toc-toggle').each( function() {
$(this).click( function() {
$(this).toggleClass('open');
});
$(this).quicksearch( this, searchElems, {
noSearchResultsIndicator: 'no-class-search-results',
focusOnLoad: false
});
$(toggle).click( function() {
// console.debug( "Toggling qsbox: %o", qsbox );
$(qsbox).toggle();
});
});
var section = $(this).next();
$(this).click( function() {
section.slideToggle();
});
});
}
function hookSearch() {
var input = $('#search-field').eq(0);
var result = $('#search-results').eq(0);
$(result).show();
var search_section = $('#search-section').get(0);
$(search_section).show();
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="' + rdoc_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) {
var result_element = result.get(0);
window.location.href = result_element.firstChild.firstChild.href;
}
search.scrollIntoView = search.scrollInWindow;
};
function highlightTarget( anchor ) {
console.debug( "Highlighting target '%s'.", anchor );
console.debug( "Highlighting target '%s'.", anchor );
$("a[name=" + anchor + "]").each( function() {
if ( !$(this).parent().parent().hasClass('target-section') ) {
console.debug( "Wrapping the target-section" );
$('div.method-detail').unwrap( 'div.target-section' );
$(this).parent().wrap( '<div class="target-section"></div>' );
} else {
console.debug( "Already wrapped." );
}
});
$("a[name=" + anchor + "]").each( function() {
if ( !$(this).parent().parent().hasClass('target-section') ) {
console.debug( "Wrapping the target-section" );
$('div.method-detail').unwrap( 'div.target-section' );
$(this).parent().wrap( '<div class="target-section"></div>' );
} else {
console.debug( "Already wrapped." );
}
});
};
function highlightLocationTarget() {
console.debug( "Location hash: %s", window.location.hash );
if ( ! window.location.hash || window.location.hash.length == 0 ) return;
console.debug( "Location hash: %s", window.location.hash );
if ( ! window.location.hash || window.location.hash.length == 0 ) return;
var anchor = window.location.hash.substring(1);
console.debug( "Found anchor: %s; matching %s", anchor, "a[name=" + anchor + "]" );
var anchor = window.location.hash.substring(1);
console.debug( "Found anchor: %s; matching %s", anchor, "a[name=" + anchor + "]" );
highlightTarget( anchor );
highlightTarget( anchor );
};
function highlightClickTarget( event ) {
console.debug( "Highlighting click target for event %o", event.target );
try {
var anchor = $(event.target).attr( 'href' ).substring(1);
console.debug( "Found target anchor: %s", anchor );
highlightTarget( anchor );
} catch ( err ) {
console.error( "Exception while highlighting: %o", err );
};
console.debug( "Highlighting click target for event %o", event.target );
try {
var anchor = $(event.target).attr( 'href' ).substring(1);
console.debug( "Found target anchor: %s", anchor );
highlightTarget( anchor );
} catch ( err ) {
console.error( "Exception while highlighting: %o", err );
};
};
$(document).ready( function() {
hookSourceViews();
hookDebuggingToggle();
hookQuickSearch();
highlightLocationTarget();
hookSourceViews();
hookDebuggingToggle();
hookSearch();
highlightLocationTarget();
hookTableOfContentsToggle();
$('ul.link-list a').bind( "click", highlightClickTarget );
$('ul.link-list a').bind( "click", highlightClickTarget );
});

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,94 @@
Search = function(data, input, result) {
this.data = data;
this.$input = $(input);
this.$result = $(result);
this.$current = null;
this.$view = this.$result.parent();
this.searcher = new Searcher(data.index);
this.init();
}
Search.prototype = $.extend({}, Navigation, new function() {
var suid = 1;
this.init = function() {
var _this = this;
var observer = function() {
_this.search(_this.$input[0].value);
};
this.$input.keyup(observer);
this.$input.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 = jQuery.trim(value).toLowerCase();
if (value) {
this.setNavigationActive(true);
} else {
this.setNavigationActive(false);
}
if (value == '') {
this.lastQuery = value;
this.$result.empty();
this.setNavigationActive(false);
} else if (value != this.lastQuery) {
this.lastQuery = value;
this.firstRun = true;
this.searcher.find(value);
}
}
this.addResults = function(results, isLast) {
var target = this.$result.get(0);
if (this.firstRun && (results.length > 0 || isLast)) {
this.$current = null;
this.$result.empty();
}
for (var i=0, l = results.length; i < l; i++) {
target.appendChild(this.renderItem.call(this, results[i]));
};
if (this.firstRun && results.length > 0) {
this.firstRun = false;
this.$current = $(target.firstChild);
this.$current.addClass('current');
}
if (jQuery.browser.msie) this.$element[0].className += '';
}
this.move = function(isDown) {
if (!this.$current) return;
var $next = this.$current[isDown ? 'next' : 'prev']();
if ($next.length) {
this.$current.removeClass('current');
$next.addClass('current');
this.scrollIntoView($next[0], this.$view[0]);
this.$current = $next;
}
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) + ';';
});
}
});

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,18 @@
<body class="file">
<nav id="metadata">
<%= render '_sidebar_navigation.rhtml' %>
<%= render '_sidebar_search.rhtml' %>
<%= render '_sidebar_table_of_contents.rhtml' %>
<div id="project-metadata">
<%= render '_sidebar_pages.rhtml' %>
<%= render '_sidebar_classes.rhtml' %>
</div>
</nav>
<div id="documentation" class="description">
<%= file.description %>
</div>

Просмотреть файл

@ -6,15 +6,14 @@
*
*/
/* vim: ft=css et sw=2 ts=2 sts=2 */
/* Base Green is: #6C8C22 */
*{ padding: 0; margin: 0; }
* { padding: 0; margin: 0; }
body {
background: #efefef;
font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
}
body.class, body.module, body.file {
margin-left: 40px;
}
body.file-popup {
@ -44,6 +43,15 @@ pre {
padding: 0.5em 0;
}
blockquote {
background: #ddd;
margin: 1em;
padding: 0.25em;
}
blockquote > :first-child {
margin-top: 0 !important;
}
/* @group Generic Classes */
@ -51,16 +59,21 @@ pre {
display: none;
}
.quicksearch-field {
#search-field {
width: 98%;
background: #ddd;
border: 1px solid #aaa;
background: #eee;
border: none;
height: 1.5em;
-webkit-border-radius: 4px;
}
.quicksearch-field:focus {
#search-field:focus {
background: #f1edba;
}
#search-field:-moz-placeholder,
#search-field::-webkit-input-placeholder {
font-weight: bold;
color: #666;
}
.missing-docs {
font-size: 120%;
@ -86,28 +99,8 @@ pre {
/* @end */
/* @group Index Page, Standalone file pages */
body.indexpage {
margin: 1em 3em;
}
body.indexpage p,
body.indexpage div,
body.file p {
margin: 1em 0;
}
.indexpage .rdoc-list p, .file .rdoc-list p {
margin: 0em 0;
}
.indexpage ol,
.file #documentation ol {
line-height: 160%;
}
.indexpage ul,
.file #documentation ul {
.indexpage ul {
line-height: 160%;
list-style: none;
}
@ -116,25 +109,16 @@ body.file p {
font-size: 16px;
}
.indexpage li,
.file #documentation li {
.indexpage li {
padding-left: 20px;
}
.indexpage ol,
.file #documentation ol {
margin-left: 20px;
}
.indexpage ol > li,
.file #documentation ol > li {
padding-left: 0;
}
.indexpage ul > li,
.file #documentation ul > li {
.indexpage ul > li {
background: url(images/bullet_black.png) no-repeat left 4px;
}
.indexpage li.method {
background: url(images/plugin.png) no-repeat left 4px;
}
.indexpage li.module {
background: url(images/package.png) no-repeat left 4px;
}
@ -144,36 +128,37 @@ body.file p {
.indexpage li.file {
background: url(images/page_white_text.png) no-repeat left 4px;
}
.file li p,
.indexpage li p {
margin: 0 0;
.indexpage li li {
background: url(images/tag_blue.png) no-repeat left 4px;
}
.indexpage li .toc-toggle {
width: 16px;
height: 16px;
background: url(images/add.png) no-repeat;
}
.indexpage li .toc-toggle.open {
background: url(images/delete.png) no-repeat;
}
/* @end */
/* @group Top-Level Structure */
.class #metadata,
.file #metadata,
.module #metadata {
#metadata {
float: left;
width: 260px;
}
.class #documentation,
.file #documentation,
.module #documentation {
#documentation {
margin: 2em 1em 5em 300px;
min-width: 340px;
}
.file #metadata {
margin: 0.8em;
}
#validator-badges {
clear: both;
margin: 1em 1em 2em;
font-size: smaller;
}
/* @end */
@ -184,7 +169,7 @@ body.file p {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border: 1px solid #aaa;
margin: 0 8px 16px;
margin: 0 8px 8px;
font-size: 90%;
overflow: hidden;
}
@ -210,11 +195,24 @@ body.file p {
list-style: none;
}
#file-metadata {
margin-top: 2em;
}
#file-metadata ul {
padding-left: 28px;
list-style-image: url(images/page_green.png);
}
#table-of-contents {
margin-top: 2em;
}
#table-of-contents ul {
padding-left: 28px;
list-style-image: url(images/tag_blue.png);
}
dl.svninfo {
color: #666;
margin: 0;
@ -225,7 +223,9 @@ dl.svninfo dt {
ul.link-list li {
white-space: nowrap;
line-height: 20px;
}
ul.link-list .type {
font-size: 8px;
text-transform: uppercase;
@ -235,16 +235,21 @@ ul.link-list .type {
-webkit-border-radius: 5px;
}
.calls-super {
background: url(images/arrow_up.png) no-repeat right center;
}
/* @end */
/* @group Class Metadata Section */
#class-metadata {
margin-top: 2em;
}
/* @end */
/* @group Project Metadata Section */
#project-metadata {
margin-top: 3em;
}
.file #project-metadata {
margin-top: 0em;
margin-top: 2em;
}
#project-metadata .section {
@ -254,33 +259,14 @@ ul.link-list .type {
border-bottom: 1px solid #aaa;
position: relative;
}
#project-metadata h3.section-header .search-toggle {
position: absolute;
right: 5px;
}
#project-metadata form {
color: #777;
background: #ccc;
padding: 8px 8px 16px;
border-bottom: 1px solid #bbb;
}
#project-metadata fieldset {
border: 0;
}
#no-class-search-results {
margin: 0 auto 1em;
text-align: center;
font-size: 14px;
font-weight: bold;
color: #aaa;
}
/* @end */
/* @group Documentation Section */
.description {
font-size: 100%;
@ -295,34 +281,44 @@ ul.link-list .type {
margin: 0;
}
.description ol,
.description ul {
margin-left: 1.5em;
}
.description ol li,
.description ul li {
line-height: 1.4em;
}
.description dl,
#documentation dl {
.note-list {
margin: 8px 0;
}
.label-list {
margin: 8px 1.5em;
border: 1px solid #ccc;
}
.description dl {
.description .label-list {
font-size: 14px;
}
.description dt,
#documentation dt {
.note-list dt {
font-weight: bold;
}
.note-list dd {
padding: 0 12px;
}
.label-list dt {
padding: 2px 4px;
font-weight: bold;
background: #ddd;
}
.description dd,
#documentation dd {
.label-list dd {
padding: 2px 12px;
}
.description dd + dt,
#documentation dd + dt {
.label-list dd + dt,
.note-list dd + dt {
margin-top: 0.7em;
}
@ -331,8 +327,8 @@ ul.link-list .type {
}
#documentation h2.section-header {
margin-top: 2em;
padding: 0.75em 0.5em;
margin-top: 1em;
padding: 0.25em 0.5em;
background: #ccc;
color: #333;
font-size: 175%;
@ -341,8 +337,25 @@ ul.link-list .type {
-webkit-border-radius: 3px;
}
.documentation-section-title {
position: relative;
}
.documentation-section-title .section-click-top {
position: absolute;
top: 6px;
right: 12px;
font-size: 10px;
color: #9b9877;
visibility: hidden;
padding-right: 0.5px;
}
.documentation-section-title:hover .section-click-top {
visibility: visible;
}
#documentation h3.section-header {
margin-top: 2em;
margin-top: 1em;
padding: 0.25em 0.5em;
background-color: #dedede;
color: #333;
@ -398,6 +411,11 @@ ul.link-list .type {
display: none;
}
#documentation .method-description .method-calls-super {
color: #333;
font-weight: bolder;
}
#documentation .method-detail {
margin: 0.5em 0;
padding: 0.5em 0;
@ -429,7 +447,7 @@ ul.link-list .type {
line-height: 20px;
background: url(images/zoom.png) no-repeat right top;
}
#documentation .method-detail:hover .method-click-advice {
#documentation .method-heading:hover .method-click-advice {
visibility: visible;
}
@ -455,14 +473,14 @@ ul.link-list .type {
cursor: default;
}
#documentation .method-description p {
padding: 0;
}
#documentation .method-description p + p {
margin-bottom: 0.5em;
}
#documentation .method-description ul {
margin-left: 1.5em;
}
pre {
margin: 0.5em 0;
}
#documentation .attribute-method-heading {
background: url(images/tag_green.png) no-repeat left bottom;
@ -481,27 +499,19 @@ ul.link-list .type {
/* @end */
/* @group Source Code */
div.method-source-code {
background: #262626;
color: #efefef;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: hidden;
}
div.method-source-code pre {
background: inherit;
padding: 0;
color: white;
pre {
overflow: auto;
background: #262626;
color: white;
border: 1px dashed #999;
padding: 0.5em;
}
/* @group Ruby keyword styles */
.description pre {
margin: 0 0.4em;
}
.ruby-constant { color: #7fffd4; background: transparent; }
.ruby-keyword { color: #00ffff; background: transparent; }
@ -509,255 +519,56 @@ div.method-source-code pre {
.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-comment { color: #dc0000; font-weight: bold; background: transparent; }
.ruby-regexp { color: #ffa07a; background: transparent; }
.ruby-value { color: #7fffd4; background: transparent; }
/* @end */
/* @end */
/* @group File Popup Contents */
.file #metadata,
.file-popup #metadata {
/* @group search results */
#search-results h1 {
font-size: 1em;
font-weight: normal;
text-shadow: none;
}
.file-popup dl {
font-size: 80%;
padding: 0.75em;
background-color: #dedede;
color: #333;
border: 1px solid #bbb;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
.file dt {
font-weight: bold;
padding-left: 22px;
line-height: 20px;
background: url(images/page_white_width.png) no-repeat left top;
}
.file dt.modified-date {
background: url(images/date.png) no-repeat left top;
}
.file dt.requires {
background: url(images/plugin.png) no-repeat left top;
}
.file dt.scs-url {
background: url(images/wrench.png) no-repeat left top;
}
.file dl dd {
margin: 0 0 1em 0;
}
.file #metadata dl dd ul {
list-style: circle;
margin-left: 20px;
padding-top: 0;
}
.file #metadata dl dd ul li {
}
.file h2 {
margin-top: 2em;
padding: 0.75em 0.5em;
background-color: #dedede;
color: #333;
font-size: 120%;
border: 1px solid #bbb;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}
/* @end */
/* @group ThickBox Styles */
#TB_window {
font: 12px Arial, Helvetica, sans-serif;
color: #333333;
}
#TB_secondLine {
font: 10px Arial, Helvetica, sans-serif;
color:#666666;
}
#TB_window :link,
#TB_window :visited { color: #666666; }
#TB_window :link:hover,
#TB_window :visited:hover { color: #000; }
#TB_window :link:active,
#TB_window :visited:active { color: #666666; }
#TB_window :link:focus,
#TB_window :visited:focus { color: #666666; }
#TB_overlay {
position: fixed;
z-index:100;
top: 0px;
left: 0px;
height:100%;
width:100%;
}
.TB_overlayMacFFBGHack {background: url(images/macFFBgHack.png) repeat;}
.TB_overlayBG {
background-color:#000;
filter:alpha(opacity=75);
-moz-opacity: 0.75;
opacity: 0.75;
}
* html #TB_overlay { /* ie6 hack */
position: absolute;
height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_window {
position: fixed;
background: #ffffff;
z-index: 102;
color:#000000;
display:none;
border: 4px solid #525252;
text-align:left;
top:50%;
left:50%;
}
* html #TB_window { /* ie6 hack */
position: absolute;
margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_window img#TB_Image {
display:block;
margin: 15px 0 0 15px;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-top: 1px solid #666;
border-left: 1px solid #666;
}
#TB_caption{
height:25px;
padding:7px 30px 10px 25px;
float:left;
}
#TB_closeWindow{
height:25px;
padding:11px 25px 10px 0;
float:right;
}
#TB_closeAjaxWindow{
padding:7px 10px 5px 0;
margin-bottom:1px;
text-align:right;
float:right;
}
#TB_ajaxWindowTitle{
float:left;
padding:7px 0 5px 10px;
margin-bottom:1px;
font-size: 22px;
}
#TB_title{
background-color: #6C8C22;
color: #dedede;
height:40px;
}
#TB_title :link,
#TB_title :visited {
color: white !important;
border-bottom: 1px dotted #dedede;
}
#TB_ajaxContent{
clear:both;
padding:2px 15px 15px 15px;
overflow:auto;
text-align:left;
line-height:1.4em;
}
#TB_ajaxContent.TB_modal{
padding:15px;
}
#TB_ajaxContent p{
padding:5px 0px 5px 0px;
}
#TB_load{
position: fixed;
display:none;
height:13px;
width:208px;
z-index:103;
top: 50%;
left: 50%;
margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
}
* html #TB_load { /* ie6 hack */
position: absolute;
margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_HideSelect{
z-index:99;
position:fixed;
top: 0;
left: 0;
background-color:#fff;
border:none;
filter:alpha(opacity=0);
-moz-opacity: 0;
opacity: 0;
height:100%;
width:100%;
}
* html #TB_HideSelect { /* ie6 hack */
position: absolute;
height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_iframeContent{
clear:both;
border:none;
margin-bottom:-1px;
margin-top:1px;
_margin-bottom:1px;
}
/* @end */
/* @group Debugging Section */
#debugging-toggle {
text-align: center;
}
#debugging-toggle img {
cursor: pointer;
}
#rdoc-debugging-section-dump {
display: none;
margin: 0 2em 2em;
#search-results .current {
background: #ccc;
border: 1px solid #999;
border-bottom: 1px solid transparent;
}
#search-results li {
list-style: none;
border-bottom: 1px solid #aaa;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
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;
}
/* @end */

Просмотреть файл

@ -0,0 +1,18 @@
<body>
<nav id="metadata">
<%= render '_sidebar_navigation.rhtml' %>
<%= render '_sidebar_search.rhtml' %>
<div id="project-metadata">
<%= render '_sidebar_pages.rhtml' %>
<%= render '_sidebar_classes.rhtml' %>
</div>
</nav>
<div id="documentation" class="description">
<h1>Not Found</h1>
<p>The page <kbd><%=h path %></kbd> was not found
</div>

Просмотреть файл

@ -0,0 +1,37 @@
<body>
<nav id="metadata">
<%= render '_sidebar_search.rhtml' %>
<%= render '_sidebar_installed.rhtml' %>
</nav>
<div id="documentation" class="description">
<h1>Local RDoc Documentation</h1>
<p>Here you can browse local documentation from the ruby standard library and
your installed gems.
<% 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 %>
</div>

Просмотреть файл

@ -0,0 +1,55 @@
<body class="indexpage">
<h1><%= h @title %></h1>
<% simple_files = @files.select { |f| f.text? } %>
<% unless simple_files.empty? then %>
<h2>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 %>
<img class="toc-toggle" src="images/transparent.png" alt="" title="toggle headings">
<ul class="initially-hidden">
<% 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/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).table_of_contents
table.concat klass.section_contents
unless table.empty? then %>
<img class="toc-toggle" src="images/transparent.png" alt="" title="toggle headings">
<ul class="initially-hidden">
<% 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 %>"><%= method.pretty_name %> &mdash; <%= method.parent.full_name %></a>
<% end %>
</ul>

Просмотреть файл

@ -0,0 +1,142 @@
/*
* 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).keydown(function(e) {
_this.onkeydown(e);
}).keyup(function(e) {
_this.onkeyup(e);
});
this.navigationActive = true;
}
this.setNavigationActive = function(state) {
this.navigationActive = state;
this.clearMoveTimeout();
}
this.onkeyup = function(e) {
if (!this.navigationActive) return;
switch(e.keyCode) {
case 37: //Event.KEY_LEFT:
case 38: //Event.KEY_UP:
case 39: //Event.KEY_RIGHT:
case 40: //Event.KEY_DOWN:
this.clearMoveTimeout();
break;
}
}
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();
this.startMoveTimeout(false);
}
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();
this.startMoveTimeout(true);
}
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.clearMoveTimeout = function() {
clearTimeout(this.moveTimeout);
this.moveTimeout = null;
}
this.startMoveTimeout = function(isDown) {
if (!$.browser.mozilla && !$.browser.opera) return;
if (this.moveTimeout) this.clearMoveTimeout();
var _this = this;
var go = function() {
if (!_this.moveTimeout) return;
_this[isDown ? 'moveDown' : 'moveUp']();
_this.moveTimout = setTimeout(go, 100);
}
this.moveTimeout = setTimeout(go, 200);
}
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);
}
}
}

Просмотреть файл

@ -0,0 +1,228 @@
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 jQuery.grep(query.split(/(\s+|::?|\(\)?)/), function(string) {
return string.match(/\S/)
});
}
function buildRegexps(queries) {
return jQuery.map(queries, function(query) {
return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i')
});
}
function buildHilighters(queries) {
return jQuery.map(queries, function(query) {
return jQuery.map(query.split(''), 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];
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) {
jQuery.each(this.handlers, function(i, fn) {
fn.call(this, results, isLast)
})
}
}

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/any_method'
##
# GhostMethod represents a method referenced only by a comment

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/code_object'
##
# A Module include in a class with \#include
@ -17,7 +15,7 @@ class RDoc::Include < RDoc::CodeObject
super()
@name = name
self.comment = comment
@module = nil # cache for module if found
@module = nil # cache for module if found
end
##
@ -30,10 +28,11 @@ class RDoc::Include < RDoc::CodeObject
end
def == other # :nodoc:
self.class == other.class and
self.name == other.name
self.class === other and @name == other.name
end
alias eql? ==
##
# Full name based on #module
@ -42,6 +41,10 @@ class RDoc::Include < RDoc::CodeObject
RDoc::ClassModule === m ? m.full_name : @name
end
def hash # :nodoc:
[@name, self.module].hash
end
def inspect # :nodoc:
"#<%s:0x%x %s.include %s>" % [
self.class,
@ -59,6 +62,13 @@ class RDoc::Include < RDoc::CodeObject
# - 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
@ -66,7 +76,7 @@ class RDoc::Include < RDoc::CodeObject
# search the current context
return @name unless parent
full_name = parent.child_name(@name)
@module = RDoc::TopLevel.modules_hash[full_name]
@module = @store.modules_hash[full_name]
return @module if @module
return @name if @name =~ /^::/
@ -76,22 +86,31 @@ class RDoc::Include < RDoc::CodeObject
inc = i.module
next if String === inc
full_name = inc.child_name(@name)
@module = RDoc::TopLevel.modules_hash[full_name]
@module = @store.modules_hash[full_name]
return @module if @module
end
# go up the hierarchy of names
p = parent.parent
while p
full_name = p.child_name(@name)
@module = RDoc::TopLevel.modules_hash[full_name]
up = parent.parent
while up
full_name = up.child_name(@name)
@module = @store.modules_hash[full_name]
return @module if @module
p = p.parent
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:
"include #@name in: #{parent}"
end

16372
lib/rdoc/markdown.rb Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,417 @@
# 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)
@string = str
@pos = 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
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? :getbyte
def get_byte
if @pos >= @string.size
return nil
end
s = @string.getbyte @pos
@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
@pos = other.pos
@string = other.string
begin
if val = __send__(rule, *args)
other.pos = @pos
other.result = @result
else
other.set_failed_rule "#{self.class}##{rule}"
end
val
ensure
@pos = old_pos
@string = old_string
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
return ans
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
return ans
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

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc'
##
# RDoc::Markup parses plain text documents and attempts to decompose them into
# their constituent parts. Some of these parts are high-level: paragraphs,
@ -8,11 +6,44 @@ require 'rdoc'
# is similar in spirit to that used on WikiWiki webs, where folks create web
# pages using a simple set of formatting rules.
#
# RDoc::Markup itself does no output formatting: this is left to a different
# set of classes.
# RDoc::Markup and other markup formats do no output formatting, this is
# handled by the RDoc::Markup::Formatter subclasses.
#
# RDoc::Markup is extendable at runtime: you can add \new markup elements to
# be recognised in the documents that RDoc::Markup parses.
# = 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
@ -26,21 +57,20 @@ require 'rdoc'
# the +convert+ method, so you can use the same RDoc::Markup converter to
# convert multiple input strings.
#
# require 'rdoc/markup/to_html'
# require 'rdoc'
#
# h = RDoc::Markup::ToHtml.new
#
# puts h.convert(input_string)
#
# You can extend the RDoc::Markup parser to recognise new markup
# 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/markup'
# require 'rdoc/markup/to_html'
# require 'rdoc'
#
# class WikiHtml < RDoc::Markup::ToHtml
# def handle_special_WIKIWORD(special)
@ -96,7 +126,12 @@ require 'rdoc'
# have been removed. In addition, the verbatim text has been shifted
# left, so the amount of indentation of verbatim text is unimportant.
#
# === Headers and Rules
# 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
@ -104,7 +139,36 @@ require 'rdoc'
# (seven hyphens or more result in a level six heading).
#
# For example, the above header was obtained with:
# == Headers and Rules
#
# === 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</h3>
#
# 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. The more hyphens, the thicker the rule
@ -240,7 +304,6 @@ require 'rdoc'
# verbatim text outside of the list (the list is therefore closed)
# regular paragraph after the list
#
#
# == Text Markup
#
# === Bold, Italic, Typewriter Text
@ -272,15 +335,26 @@ require 'rdoc'
# === Links
#
# Links to starting with +http:+, +https:+, +mailto:+, +ftp:+ or +www.+
# are recognized. An HTTP url that references an external image file is
# converted into an inline image element.
# are recognized. An HTTP url that references an external image is converted
# into an inline image element.
#
# Links starting with <tt>rdoc-ref:</tt> will link to the referenced class,
# module, method, file, etc. If the referenced item is not documented the
# text will be and no link will be generated.
# 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).
#
# Links starting with +link:+ refer to local files whose path is relative to
# the <tt>--op</tt> directory.
# 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.
#
# 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+
@ -293,6 +367,11 @@ require 'rdoc'
# 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
@ -492,27 +571,54 @@ require 'rdoc'
# so you won't see the documentation unless you use the +-a+ command line
# option.
#
# === Other directives
# === Method arguments
#
# [+: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.
# [+:arg:+ or +:args:+ _parameters_]
# Overrides the default argument handling with exactly these parameters.
#
# 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.
# ##
# # :args: a, b
#
# [+: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).
# def some_method(*a)
# end
#
# [+:main:+ _name_]
# Equivalent to the <tt>--main</tt> command line parameter.
# [+:yield:+ or +:yields:+ _parameters_]
# Overrides the default yield discovery with these parameters.
#
# [<tt>:category: section</tt>]
# ##
# # :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).
@ -541,7 +647,7 @@ require 'rdoc'
# Use the :section: directive to provide introductory text for a section of
# documentation.
#
# [<tt>:section: title</tt>]
# [+: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
@ -573,12 +679,60 @@ require 'rdoc'
# # ...
# end
#
# [+:call-seq:+]
# Lines up to the next blank line in the comment are treated as the method's
# calling sequence, overriding the default parsing of method parameters and
# yield arguments.
# === 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@DEVELOPERS 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.
#
# Further directives can be found in RDoc::Parser::Ruby and RDoc::Parser::C.
#--
# Original Author:: Dave Thomas, dave@pragmaticprogrammer.com
# License:: Ruby license
@ -590,6 +744,34 @@ class RDoc::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/rdoc/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
@ -644,9 +826,47 @@ class RDoc::Markup
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
require 'rdoc/markup/parser'
require 'rdoc/markup/attribute_manager'
require 'rdoc/markup/inline'

Просмотреть файл

@ -0,0 +1,22 @@
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

Просмотреть файл

@ -0,0 +1,29 @@
##
# 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

Просмотреть файл

@ -22,6 +22,11 @@ class RDoc::Markup::AttributeManager
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
@ -60,8 +65,9 @@ class RDoc::Markup::AttributeManager
@html_tags = {}
@matching_word_pairs = {}
@protectable = %w[<]
@special = {}
@special = []
@word_pair_map = {}
@attributes = RDoc::Markup::Attributes.new
add_word_pair "*", "*", :BOLD
add_word_pair "_", "_", :EM
@ -96,11 +102,11 @@ class RDoc::Markup::AttributeManager
def changed_attribute_by_name current_set, new_set
current = new = 0
current_set.each do |name|
current |= RDoc::Markup::Attribute.bitmap_for(name)
current |= @attributes.bitmap_for(name)
end
new_set.each do |name|
new |= RDoc::Markup::Attribute.bitmap_for(name)
new |= @attributes.bitmap_for(name)
end
change_attribute(current, new)
@ -161,12 +167,15 @@ class RDoc::Markup::AttributeManager
##
# Converts special sequences to RDoc attributes
def convert_specials(str, attrs)
def convert_specials str, attrs
unless @special.empty?
@special.each do |regexp, attr|
@special.each do |regexp, attribute|
str.scan(regexp) do
attrs.set_attrs($`.length, $&.length,
attr | RDoc::Markup::Attribute::SPECIAL)
capture = $~.size == 1 ? 0 : 1
s, e = $~.offset capture
attrs.set_attrs s, e - s, attribute | @attributes.special
end
end
end
@ -200,7 +209,7 @@ class RDoc::Markup::AttributeManager
raise ArgumentError, "Word flags may not start with '<'" if
start[0,1] == '<'
bitmap = RDoc::Markup::Attribute.bitmap_for name
bitmap = @attributes.bitmap_for name
if start == stop then
@matching_word_pairs[start] = bitmap
@ -220,7 +229,7 @@ class RDoc::Markup::AttributeManager
# am.add_html 'em', :EM
def add_html(tag, name)
@html_tags[tag.downcase] = RDoc::Markup::Attribute.bitmap_for name
@html_tags[tag.downcase] = @attributes.bitmap_for name
end
##
@ -229,14 +238,14 @@ class RDoc::Markup::AttributeManager
#
# @am.add_special(/((https?:)\S+\w)/, :HYPERLINK)
def add_special(pattern, name)
@special[pattern] = RDoc::Markup::Attribute.bitmap_for name
def add_special pattern, name
@special << [pattern, @attributes.bitmap_for(name)]
end
##
# Processes +str+ converting attributes, HTML and specials
def flow(str)
def flow str
@str = str
mask_protected_sequences
@ -303,9 +312,9 @@ class RDoc::Markup::AttributeManager
res << change_attribute(current_attr, new_attr)
current_attr = new_attr
if (current_attr & RDoc::Markup::Attribute::SPECIAL) != 0 then
if (current_attr & @attributes.special) != 0 then
i += 1 while
i < str_len and (@attrs[i] & RDoc::Markup::Attribute::SPECIAL) != 0
i < str_len and (@attrs[i] & @attributes.special) != 0
res << RDoc::Markup::Special.new(current_attr,
copy_string(start_pos, i))

Просмотреть файл

@ -0,0 +1,70 @@
##
# 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

Просмотреть файл

@ -0,0 +1,14 @@
##
# 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

Просмотреть файл

@ -3,11 +3,13 @@
class RDoc::Markup::Document
include Enumerable
##
# The file this document was created from. See also
# RDoc::ClassModule#add_comment
attr_accessor :file
attr_reader :file
##
# The parts of the Document
@ -19,7 +21,7 @@ class RDoc::Markup::Document
def initialize *parts
@parts = []
@parts.push(*parts)
@parts.concat parts
@file = nil
end
@ -31,7 +33,7 @@ class RDoc::Markup::Document
case part
when RDoc::Markup::Document then
unless part.empty? then
parts.push(*part.parts)
parts.concat part.parts
parts << RDoc::Markup::BlankLine.new
end
when String then
@ -67,6 +69,20 @@ class RDoc::Markup::Document
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?
@ -74,6 +90,18 @@ class RDoc::Markup::Document
@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.absolute_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
@ -120,7 +148,16 @@ class RDoc::Markup::Document
# Appends +parts+ to the document
def push *parts
self.parts.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

Просмотреть файл

@ -1,9 +1,9 @@
require 'rdoc/markup'
##
# Base class for RDoc markup formatters
#
# Formatters use a visitor pattern to convert content into output.
# 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
@ -20,14 +20,21 @@ class RDoc::Markup::Formatter
##
# Creates a new Formatter
def initialize markup = nil
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 = RDoc::Markup::Attribute.bitmap_for :TT
@tt_bit = @attributes.bitmap_for :TT
@hard_break = ''
end
##
@ -44,7 +51,7 @@ class RDoc::Markup::Formatter
# tags for flexibility
def add_tag(name, start, stop)
attr = RDoc::Markup::Attribute.bitmap_for name
attr = @attributes.bitmap_for name
@attr_tags << InlineTag.new(attr, start, stop)
end
@ -58,7 +65,7 @@ class RDoc::Markup::Formatter
##
# Marks up +content+
def convert(content)
def convert content
@markup.convert content, self
end
@ -93,7 +100,7 @@ class RDoc::Markup::Formatter
handled = false
RDoc::Markup::Attribute.each_name_of special.type do |name|
@attributes.each_name_of special.type do |name|
method_name = "handle_special_#{name}"
if respond_to? method_name then
@ -102,7 +109,11 @@ class RDoc::Markup::Formatter
end
end
raise "Unhandled special: #{special}" unless handled
unless handled then
special_name = @attributes.as_string special.type
raise RDoc::Error, "Unhandled special #{special_name}: #{special}"
end
special.text
end
@ -114,6 +125,17 @@ class RDoc::Markup::Formatter
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?
@ -160,10 +182,3 @@ class RDoc::Markup::Formatter
end
class RDoc::Markup
autoload :ToAnsi, 'rdoc/markup/to_ansi'
autoload :ToBs, 'rdoc/markup/to_bs'
autoload :ToHtml, 'rdoc/markup/to_html'
autoload :ToHtmlCrossref, 'rdoc/markup/to_html_crossref'
autoload :ToRdoc, 'rdoc/markup/to_rdoc'
end

Просмотреть файл

@ -1,5 +1,4 @@
require 'minitest/unit'
require 'rdoc/markup/formatter'
##
# Test case for creating new RDoc::Markup formatters. See
@ -35,7 +34,7 @@ require 'rdoc/markup/formatter'
#
# end
class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
class RDoc::Markup::FormatterTestCase < RDoc::TestCase
##
# Call #setup when inheriting from this test case.
@ -54,8 +53,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
def setup
super
@m = RDoc::Markup.new
@RM = RDoc::Markup
@options = RDoc::Options.new
@m = @RM.new
@bullet_list = @RM::List.new(:BULLET,
@RM::ListItem.new(nil, @RM::Paragraph.new('l1')),
@ -86,7 +86,7 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
# Call to add the visitor tests to your test case
def self.add_visitor_tests
self.class_eval do
class_eval do
##
# Calls start_accepting which needs to verify startup state
@ -119,6 +119,16 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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>
@ -233,6 +243,29 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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
@ -374,9 +407,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
# Calls accept_list_item_start_note_2
def test_accept_list_item_start_note_2
list = @RM::List.new(:NOTE,
@RM::ListItem.new('<tt>teletype</tt>',
@RM::Paragraph.new('teletype description')))
list = list(:NOTE,
item('<tt>teletype</tt>',
para('teletype description')))
@to.start_accepting
@ -387,6 +420,41 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
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
@ -635,7 +703,7 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
end
##
# Calls accept_list_end_ulpha
# Calls accept_list_end_ualpha
def test_accept_list_end_ualpha
@to.start_accepting
@ -670,28 +738,28 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
# Calls list_verbatim with a list containing a verbatim block
def test_list_verbatim # HACK overblown
doc = @RM::Document.new(
@RM::List.new(:BULLET,
@RM::ListItem.new(nil,
@RM::Paragraph.new('list', 'stuff'),
@RM::BlankLine.new,
@RM::Verbatim.new("* list\n",
" with\n",
"\n",
" second\n",
"\n",
" 1. indented\n",
" 2. numbered\n",
"\n",
" third\n",
"\n",
"* second\n"))))
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

Просмотреть файл

@ -0,0 +1,31 @@
##
# 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

Просмотреть файл

@ -3,6 +3,35 @@
class RDoc::Markup::Heading < Struct.new :level, :text
@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+
@ -10,6 +39,21 @@ class RDoc::Markup::Heading < Struct.new :level, :text
visitor.accept_heading self
end
##
# An HTML-safe anchor reference for this header.
def aref
"label-#{self.class.to_label.convert text.dup}"
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

Просмотреть файл

@ -0,0 +1,42 @@
##
# 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

Просмотреть файл

@ -29,5 +29,19 @@ class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
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

Просмотреть файл

@ -1,144 +1 @@
require 'rdoc'
class RDoc::Markup
##
# We manage a set of attributes. Each attribute has a symbol name and a bit
# value.
class Attribute
##
# Special attribute type. See RDoc::Markup#add_special
SPECIAL = 1
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
@@next_bitmap = 2
##
# Returns a unique bit for +name+
def self.bitmap_for(name)
bitmap = @@name_to_bitmap[name]
unless bitmap then
bitmap = @@next_bitmap
@@next_bitmap <<= 1
@@name_to_bitmap[name] = bitmap
end
bitmap
end
##
# Returns a string representation of +bitmap+
def self.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 self.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
AttrChanger = Struct.new :turn_on, :turn_off # :nodoc:
##
# An AttrChanger records a change in attributes. It contains a bitmap of the
# attributes to turn on, and a bitmap of those to turn off.
class AttrChanger
def to_s # :nodoc:
"Attr: +#{Attribute.as_string turn_on}/-#{Attribute.as_string turn_off}"
end
def inspect # :nodoc:
"+%s/-%s" % [
Attribute.as_string(turn_on),
Attribute.as_string(turn_off),
]
end
end
##
# An array of attributes which parallels the characters in a string.
class AttrSpan
##
# Creates a new AttrSpan for +length+ characters
def initialize(length)
@attrs = Array.new(length, 0)
end
##
# Toggles +bits+ from +start+ to +length+
def set_attrs(start, length, bits)
for i in start ... (start+length)
@attrs[i] |= bits
end
end
##
# Accesses flags for character +n+
def [](n)
@attrs[n]
end
end
##
# Hold details of a special sequence
class Special
##
# Special type
attr_reader :type
##
# Special text
attr_accessor :text
##
# Creates a new special sequence of +type+ with +text+
def initialize(type, text)
@type, @text = type, text
end
##
# Specials are equal when the have the same text and type
def ==(o)
self.text == o.text && self.type == o.type
end
def inspect # :nodoc:
"#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
end
def to_s # :nodoc:
"Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
end
end
end
warn "requiring rdoc/markup/inline is deprecated and will be removed in RDoc 4." if $-w

Просмотреть файл

@ -1,5 +1,24 @@
##
# A List of ListItems
# 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
@ -14,12 +33,13 @@ class RDoc::Markup::List
attr_reader :items
##
# Creates a new list of +type+ with +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.push(*items)
@items.concat items
end
##
@ -74,7 +94,7 @@ class RDoc::Markup::List
# Appends +items+ to the list
def push *items
@items.push(*items)
@items.concat items
end
end

Просмотреть файл

@ -1,5 +1,12 @@
##
# 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
@ -19,7 +26,7 @@ class RDoc::Markup::ListItem
def initialize label = nil, *parts
@label = label
@parts = []
@parts.push(*parts)
@parts.concat parts
end
##
@ -64,8 +71,14 @@ class RDoc::Markup::ListItem
def pretty_print q # :nodoc:
q.group 2, '[item: ', ']' do
if @label then
q.text @label
case @label
when Array then
q.pp @label
q.text ';'
q.breakable
when String then
q.pp @label
q.text ';'
q.breakable
end
@ -79,7 +92,7 @@ class RDoc::Markup::ListItem
# Adds +parts+ to the ListItem
def push *parts
@parts.push(*parts)
@parts.concat parts
end
end

Просмотреть файл

@ -10,5 +10,19 @@ class RDoc::Markup::Paragraph < RDoc::Markup::Raw
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

Просмотреть файл

@ -1,5 +1,4 @@
require 'strscan'
require 'rdoc/text'
##
# A recursive-descent parser for RDoc markup.
@ -52,7 +51,9 @@ class RDoc::Markup::Parser
attr_reader :tokens
##
# Parses +str+ into a Document
# Parses +str+ into a Document.
#
# Use RDoc::Markup#parse instead of this method.
def self.parse str
parser = new
@ -74,12 +75,15 @@ class RDoc::Markup::Parser
# Creates a new Parser. See also ::parse
def initialize
@tokens = []
@current_token = nil
@debug = false
@line = 0
@line_pos = 0
@binary_input = nil
@current_token = nil
@debug = false
@have_encoding = Object.const_defined? :Encoding
@input_encoding = nil
@line = 0
@line_pos = 0
@s = nil
@tokens = []
end
##
@ -107,13 +111,13 @@ class RDoc::Markup::Parser
p :list_start => margin if @debug
list = RDoc::Markup::List.new
label = nil
until @tokens.empty? do
type, data, column, = get
case type
when :BULLET, :LABEL, :LALPHA, :NOTE, :NUMBER, :UALPHA then
when *LIST_TOKENS then
if column < margin || (list.type && list.type != type) then
unget
break
@ -124,6 +128,8 @@ class RDoc::Markup::Parser
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
@ -146,32 +152,35 @@ class RDoc::Markup::Parser
# In all cases, we have an empty description.
# In the last case only, we continue.
if peek_type.nil? || column < margin then
empty = 1
empty = true
elsif column == margin then
case peek_type
when type
empty = 2 # continue
empty = :continue
when *LIST_TOKENS
empty = 1
empty = true
else
empty = 0
empty = false
end
else
empty = 0
empty = false
end
if empty > 0 then
item = RDoc::Markup::ListItem.new(data)
item << RDoc::Markup::BlankLine.new
list << item
break if empty == 1
next
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
@ -184,7 +193,13 @@ class RDoc::Markup::Parser
p :list_end => margin if @debug
return nil if list.empty?
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
@ -200,15 +215,20 @@ class RDoc::Markup::Parser
until @tokens.empty? do
type, data, column, = get
if type == :TEXT && column == margin then
if type == :TEXT and column == margin then
paragraph << data
skip :NEWLINE
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
@ -267,7 +287,7 @@ class RDoc::Markup::Parser
peek_column ||= column + width
indent = peek_column - column - width
line << ' ' * indent
when :TEXT then
when :BREAK, :TEXT then
line << data
else # *LIST_TOKENS
list_marker = case type
@ -297,6 +317,19 @@ class RDoc::Markup::Parser
verbatim
end
##
# The character offset for the input string at the given +byte_offset+
def char_pos byte_offset
if @have_encoding then
matched = @binary_input[0, byte_offset]
matched.force_encoding @input_encoding
matched.length
else
byte_offset
end
end
##
# Pulls the next token from the stream.
@ -321,7 +354,12 @@ class RDoc::Markup::Parser
until @tokens.empty? do
type, data, column, = get
if type == :NEWLINE then
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
@ -372,6 +410,21 @@ class RDoc::Markup::Parser
token
end
##
# Creates the StringScanner
def setup_scanner input
@line = 0
@line_pos = 0
if @have_encoding then
@input_encoding = input.encoding
@binary_input = input.dup.force_encoding Encoding::BINARY
end
@s = StringScanner.new input
end
##
# Skips the next token if its type is +token_type+.
#
@ -389,58 +442,55 @@ class RDoc::Markup::Parser
# Turns text +input+ into a stream of tokens
def tokenize input
s = StringScanner.new input
setup_scanner input
@line = 0
@line_pos = 0
until s.eos? do
pos = s.pos
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(/ +/)
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 = s.pos
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
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
if @s[2] =~ /^\r?\n/ then
@s.pos -= @s[2].length
header
else
pos = s.pos
s.scan(/.*/)
pos = @s.pos
@s.scan(/.*/)
@tokens << header
[:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
[:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)]
end
# --- (at least 3) and nothing else on the line => :RULE
when s.scan(/(-{3,}) *$/) then
[:RULE, s[1].length - 2, *token_pos(pos)]
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)]
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
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_label = @s[1]
@s.pos -= @s[2].bytesize # unget \S
list_type =
case list_label
when /[a-z]/ then :LALPHA
@ -451,14 +501,21 @@ class RDoc::Markup::Parser
end
[list_type, list_label, *token_pos(pos)]
# [text] followed by spaces or end of line => :LABEL
when s.scan(/\[(.*?)\]( +|$)/) then
[:LABEL, s[1], *token_pos(pos)]
when @s.scan(/\[(.*?)\]( +|\r?$)/) then
[:LABEL, @s[1], *token_pos(pos)]
# text:: followed by spaces or end of line => :NOTE
when s.scan(/(.*?)::( +|$)/) then
[:NOTE, s[1], *token_pos(pos)]
when @s.scan(/(.*?)::( +|\r?$)/) then
[:NOTE, @s[1], *token_pos(pos)]
# anything else: :TEXT
else s.scan(/.*/)
[:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
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
@ -466,9 +523,12 @@ class RDoc::Markup::Parser
end
##
# Calculates the column and line of the current token based on +offset+.
# Calculates the column (by character) and line of the current token from
# +scanner+ based on +byte_offset+.
def token_pos byte_offset
offset = char_pos byte_offset
def token_pos offset
[offset - @line_pos, @line]
end
@ -484,14 +544,3 @@ class RDoc::Markup::Parser
end
require 'rdoc/markup/blank_line'
require 'rdoc/markup/document'
require 'rdoc/markup/heading'
require 'rdoc/markup/list'
require 'rdoc/markup/list_item'
require 'rdoc/markup/raw'
require 'rdoc/markup/paragraph'
require 'rdoc/markup/indented_paragraph'
require 'rdoc/markup/rule'
require 'rdoc/markup/verbatim'

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/markup'
require 'rdoc/encoding'
##
# Handle common directives that can occur in a block of text:
#
@ -9,18 +6,48 @@ require 'rdoc/encoding'
# 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
# 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
@registered = {}
##
# 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
@ -33,6 +60,16 @@ class RDoc::Markup::PreProcess
@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+
@ -44,7 +81,7 @@ class RDoc::Markup::PreProcess
end
##
# Look for directives in a chunk of +text+.
# 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
@ -54,27 +91,56 @@ class RDoc::Markup::PreProcess
# If no matching directive was registered the directive is restored to the
# text.
#
# If +code_object+ is given and the param is set as metadata on the
# +code_object+. See RDoc::CodeObject#metadata
# 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
encoding = if defined?(Encoding) then text.encoding else nil end
if RDoc::Comment === text then
comment = text
text = text.text
end
encoding = text.encoding if defined?(Encoding)
# regexp helper (square brackets for optional)
# $1 $2 $3 $4 $5
# [prefix][\]:directive:[spaces][param]newline
text.gsub!(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?\n/) do
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, encoding, &block
end
comment = text unless comment
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 ''
@ -92,7 +158,7 @@ class RDoc::Markup::PreProcess
blankline
when 'category' then
if RDoc::Context === code_object then
section = code_object.add_section param, ''
section = code_object.add_section param
code_object.temporary_section = section
end

Просмотреть файл

@ -13,7 +13,7 @@ class RDoc::Markup::Raw
def initialize *parts
@parts = []
@parts.push(*parts)
@parts.concat parts
end
##
@ -24,7 +24,7 @@ class RDoc::Markup::Raw
end
def == other # :nodoc:
self.class == other.class and text == other.text
self.class == other.class and @parts == other.parts
end
##
@ -38,11 +38,11 @@ class RDoc::Markup::Raw
# Appends +other+'s parts
def merge other
@parts.push(*other.parts)
@parts.concat other.parts
end
def pretty_print q # :nodoc:
self.class.name =~ /.*::(\w{4})/i
self.class.name =~ /.*::(\w{1,4})/i
q.group 2, "[#{$1.downcase}: ", ']' do
q.seplist @parts do |part|
@ -55,7 +55,7 @@ class RDoc::Markup::Raw
# Appends +texts+ onto this Paragraph
def push *texts
self.parts.push(*texts)
self.parts.concat texts
end
##

Просмотреть файл

@ -0,0 +1,40 @@
##
# 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

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/markup/formatter_test_case'
##
# Test case for creating new plain-text RDoc::Markup formatters. See also
# RDoc::Markup::FormatterTestCase

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/markup/to_rdoc'
##
# Outputs RDoc markup with vibrant ANSI color!
@ -34,6 +32,11 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
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
@ -53,7 +56,13 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
when :BULLET then
'*'
when :NOTE, :LABEL then
attributes(list_item.label) + ":\n"
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

Просмотреть файл

@ -1,5 +1,3 @@
require 'rdoc/markup/to_rdoc'
##
# Outputs RDoc markup with hot backspace action! You will probably need a
# pager to use this output format.

Просмотреть файл

@ -1,10 +1,7 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/inline'
require 'cgi'
##
# Outputs RDoc markup as HTML
# Outputs RDoc markup as HTML.
class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@ -16,18 +13,24 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
LIST_TYPE_TO_HTML = {
:BULLET => ['<ul>', '</ul>'],
:LABEL => ['<dl class="rdoc-list">', '</dl>'],
:LALPHA => ['<ol style="display: lower-alpha">', '</ol>'],
:NOTE => ['<table class="rdoc-list">', '</table>'],
:NUMBER => ['<ol>', '</ol>'],
:UALPHA => ['<ol style="display: upper-alpha">', '</ol>'],
: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
@ -62,19 +65,31 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
##
# Creates a new formatter that will output HTML
def initialize markup = nil
def initialize options, markup = nil
super
@th = nil
@code_object = nil
@from_path = ''
@in_list_entry = nil
@list = nil
@from_path = ''
@th = nil
@hard_break = "<br>\n"
# external links
@markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
@markup.add_special(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)\S+\w/,
:HYPERLINK)
# internal links
@markup.add_special(/rdoc-[a-z]+:\S+/, :RDOCLINK)
# and links of the form <text>[<url>]
@markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\])/, :TIDYLINK)
@markup.add_special(/(?:
\{.*?\} | # multi-word label
\b[^\s{}]+? # single-word label
)
\[\S+?\] # link target
/x, :TIDYLINK)
init_tags
end
@ -83,6 +98,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
#
# These methods handle special markup added by RDoc::Markup#add_special.
##
# +special+ is a <code><br></code>
def handle_special_HARD_BREAK special
'<br>'
end
##
# +special+ is a potential link. The following schemes are handled:
#
@ -101,6 +123,39 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
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
url = special.text
case url
when /\Ardoc-ref:/
$'
when /\Ardoc-label:/
text = $'
text = case text
when /\Alabel-/ then $'
when /\Afootmark-/ then "^#{$'}"
when /\Afoottext-/ then "*#{$'}"
else text
end
gen_url url, text
else
url =~ /\Ardoc-[a-z]+:/
$'
end
end
##
# This +special+ is a link where the label is different from the URL
# <tt>label[url]</tt> or <tt>{long label}[url]</tt>
@ -135,22 +190,48 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@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)
def accept_paragraph paragraph
@res << "\n<p>"
@res << wrap(to_html(paragraph.text))
text = paragraph.text @hard_break
@res << wrap(to_html(text))
@res << "</p>\n"
end
##
# Adds +verbatim+ to the output
def accept_verbatim(verbatim)
@res << "\n<pre>"
@res << CGI.escapeHTML(verbatim.text.rstrip)
@res << "</pre>\n"
def accept_verbatim verbatim
text = verbatim.text.rstrip
@res << if verbatim.ruby? or parseable? text then
begin
tokens = RDoc::RubyLex.tokenize text, @options
html = RDoc::TokenStream.to_html tokens
"\n<pre class=\"ruby\">#{html}</pre>\n"
rescue RDoc::RubyLex::Error
"\n<pre>#{CGI.escapeHTML text}</pre>\n"
end
else
"\n<pre>#{CGI.escapeHTML text}</pre>\n"
end
end
##
@ -208,12 +289,19 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
##
# Adds +heading+ to the output
# Adds +heading+ to the output. The headings greater than 6 are trimmed to
# level 6.
def accept_heading(heading)
@res << "\n<h#{heading.level}>"
def accept_heading heading
level = [6, heading.level].min
label = heading.aref
label = [@code_object.aref, label].compact.join '-' if
@code_object and @code_object.respond_to? :aref
@res << "\n<h#{level} id=\"#{label}\">"
@res << to_html(heading.text)
@res << "</h#{heading.level}>\n"
@res << "</h#{level}>\n"
end
##
@ -226,18 +314,22 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# :section: Utilities
##
# CGI escapes +text+
# CGI-escapes +text+
def convert_string(text)
CGI.escapeHTML text
end
##
# Generate a link for +url+, labeled with +text+. Handles the special cases
# 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)
if url =~ /([A-Za-z]+):(.*)/ then
def gen_url url, text
if url =~ /^rdoc-label:([^:]*)(?::(.*))?/ then
type = "link"
path = "##{$1}"
id = " id=\"#{$2}\"" if $2
elsif url =~ /([A-Za-z]+):(.*)/ then
type = $1
path = $2
else
@ -258,7 +350,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
"<img src=\"#{url}\" />"
else
"<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
"<a#{id} href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
end
end
@ -275,9 +367,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# Maps attributes to HTML tags
def init_tags
add_tag :BOLD, "<b>", "</b>"
add_tag :TT, "<tt>", "</tt>"
add_tag :EM, "<em>", "</em>"
add_tag :BOLD, "<strong>", "</strong>"
add_tag :TT, "<code>", "</code>"
add_tag :EM, "<em>", "</em>"
end
##
@ -288,10 +380,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
"<li>"
when :LABEL then
"<dt>#{to_html list_item.label}</dt>\n<dd>"
when :NOTE then
"<tr><td class=\"rdoc-term\"><p>#{to_html list_item.label}</p></td>\n<td>"
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
@ -304,15 +396,21 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
"</li>"
when :LABEL then
when :LABEL, :NOTE then
"</dd>"
when :NOTE then
"</td></tr>"
else
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
##
# Returns true if Ripper is available it can create a sexp from +text+
def parseable? text
text =~ /\b(def|class|module|require) |=>|\{\s?\||do \|/ and
text !~ /<%|%>/
end
##
# Converts +item+ to HTML using RDoc::Text#to_html

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/markup/to_html'
require 'rdoc/cross_reference'
##
# Subclass of the RDoc::Markup::ToHtml class that supports looking up method
# names, classes, etc to create links. RDoc::CrossReference is used to
@ -31,21 +28,20 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# references are removed unless +show_hash+ is true. Only method names
# preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
def initialize(from_path, context, show_hash, hyperlink_all = false,
markup = nil)
def initialize(options, from_path, context, markup = nil)
raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super markup
crossref_re = hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
@cross_reference = RDoc::CrossReference.new context
@markup.add_special crossref_re, :CROSSREF
@markup.add_special(/rdoc-ref:\S+\w/, :HYPERLINK)
super options, markup
@context = context
@from_path = from_path
@hyperlink_all = hyperlink_all
@show_hash = show_hash
@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
##
@ -57,6 +53,8 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
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
@ -72,6 +70,8 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
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
@ -92,6 +92,25 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
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.
@ -106,13 +125,31 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# 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
"<a href=\"#{ref.as_href @from_path}\">#{text}</a>"
path = ref.as_href @from_path
if path =~ /#/ then
path << "-label-#{label}"
else
path << "#label-#{label}"
end if label
"<a href=\"#{path}\">#{text}</a>"
end
end

Просмотреть файл

@ -0,0 +1,284 @@
##
# 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

Просмотреть файл

@ -0,0 +1,68 @@
##
# 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
end
def end_accepting
end
def accept_paragraph paragraph
parts = []
string = false
paragraph.parts.each do |part|
if String === part then
if string then
string << part
else
parts << part
string = part
end
else
parts << part
string = false
end
end
parts = parts.map do |part|
if String === part then
part.rstrip
else
part
end
end
# TODO use Enumerable#chunk when ruby 1.8 support is dropped
#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

Просмотреть файл

@ -0,0 +1,74 @@
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
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

Просмотреть файл

@ -0,0 +1,134 @@
# :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] = ['###### ', '']
@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
end

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/inline'
##
# Outputs RDoc markup as RDoc markup! (mostly)
@ -45,7 +42,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
# Creates a new formatter that will output (mostly) \RDoc markup
def initialize markup = nil
super
super nil, markup
@markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
@width = 78
@ -60,6 +57,8 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@headings[4] = ['==== ', '']
@headings[5] = ['===== ', '']
@headings[6] = ['====== ', '']
@hard_break = "\n"
end
##
@ -78,6 +77,21 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@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
@ -106,6 +120,11 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
when :BULLET then
2
when :NOTE, :LABEL then
if @prefix then
@res << @prefix.strip
@prefix = nil
end
@res << "\n"
2
else
@ -125,10 +144,15 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
case type
when :NOTE, :LABEL then
bullet = attributes(list_item.label) + ":\n"
bullets = Array(list_item.label).map do |label|
attributes(label).strip
end.join "\n"
bullets << ":\n" unless bullets.empty?
@prefix = ' ' * @indent
@indent += 2
@prefix << bullet + (' ' * @indent)
@prefix << bullets + (' ' * @indent)
else
bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
@prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
@ -168,7 +192,8 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
# Adds +paragraph+ to the output
def accept_paragraph paragraph
wrap attributes(paragraph.text)
text = paragraph.text @hard_break
wrap attributes text
end
##
@ -176,7 +201,8 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
def accept_indented_paragraph paragraph
@indent += paragraph.indent
wrap attributes(paragraph.text)
text = paragraph.text @hard_break
wrap attributes text
@indent -= paragraph.indent
end
@ -234,6 +260,13 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
text
end
##
# Adds a newline to the output
def handle_special_HARD_BREAK special
"\n"
end
##
# Prepares the visitor for text generation
@ -252,8 +285,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
# prefix for later consumption.
def use_prefix
prefix = @prefix
@prefix = nil
prefix, @prefix = @prefix, nil
@res << prefix if prefix
prefix

Просмотреть файл

@ -0,0 +1,61 @@
##
# 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
def initialize # :nodoc:
super nil
end
##
# Adds +heading+ to the table of contents
def accept_heading heading
@res << heading
end
##
# Returns the table of contents
def end_accepting
@res
end
##
# Prepares the visitor for text generation
def start_accepting
@res = []
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

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/markup'
require 'rdoc/markup/formatter'
##
# This Markup outputter is used for testing purposes.

Просмотреть файл

@ -1,6 +1,3 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/inline'
##
# Extracts sections of text enclosed in plus, tt or code. Used to discover
# undocumented parameters.
@ -21,11 +18,18 @@ class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
# Creates a new tt-only formatter.
def initialize markup = nil
super
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
@ -46,7 +50,9 @@ class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter
def accept_list_item_start list_item
case @list_type.last
when :NOTE, :LABEL then
tt_sections(list_item.label)
Array(list_item.label).map do |label|
tt_sections label
end.flatten
end
end

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше