* lib/rdoc/markup/to_joined_paragraph.rb: Completed documentation

* lib/rdoc/parser/c.rb:  ditto
* lib/rdoc/parser/changelog.rb:  ditto
* lib/rdoc/servlet.rb:  ditto
* lib/rdoc/store.rb:  ditto

* lib/rdoc/store.rb:  Improved HTML error page.  Completed
  documentation

* lib/rdoc/parser/ruby.rb:  Fixed bug attaching a comment to A::B = 42
* test/rdoc/test_rdoc_parser_ruby.rb:  Test for above

* test/rdoc/test_rdoc_comment.rb:  Removed garbage


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38256 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2012-12-07 05:22:50 +00:00
Родитель 08f0db2c68
Коммит 85e3560a3b
9 изменённых файлов: 381 добавлений и 25 удалений

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

@ -1,3 +1,19 @@
Fri Dec 7 14:22:29 2012 Eric Hodel <drbrain@segment7.net>
* lib/rdoc/markup/to_joined_paragraph.rb: Completed documentation
* lib/rdoc/parser/c.rb: ditto
* lib/rdoc/parser/changelog.rb: ditto
* lib/rdoc/servlet.rb: ditto
* lib/rdoc/store.rb: ditto
* lib/rdoc/store.rb: Improved HTML error page. Completed
documentation
* lib/rdoc/parser/ruby.rb: Fixed bug attaching a comment to A::B = 42
* test/rdoc/test_rdoc_parser_ruby.rb: Test for above
* test/rdoc/test_rdoc_comment.rb: Removed garbage
Fri Dec 7 14:03:59 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* lib/timeout.rb (Timeout#timeout): since async_interrupt_timing

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

@ -12,12 +12,15 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
super nil
end
def start_accepting
def start_accepting # :nodoc:
end
def end_accepting
def end_accepting # :nodoc:
end
##
# Converts the parts of +paragraph+ to a single entry.
def accept_paragraph paragraph
parts = []
string = false

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

@ -446,6 +446,10 @@ class RDoc::Parser::C < RDoc::Parser
end
end
##
# Creates classes and module that were missing were defined due to the file
# order being different than the declaration order.
def do_missing
return if @missing_dependencies.empty?

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

@ -1,11 +1,28 @@
require 'time'
##
# A ChangeLog file parser.
#
# This parser converts a ChangeLog into an RDoc::Markup::Document. When
# viewed as HTML a ChangeLog page will have an entry for each day's entries in
# the sidebar table of contents.
#
# This parser is meant to parse the MRI ChangeLog, but can be used to parse any
# {GNU style Change
# Log}[http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html].
class RDoc::Parser::ChangeLog < RDoc::Parser
include RDoc::Parser::Text
parse_files_matching(/(\/|\\|\A)ChangeLog[^\/\\]*\z/)
##
# Attaches the +continuation+ of the previous line to the +entry_body+.
#
# Continued function listings are joined together as a single entry.
# Continued descriptions are joined to make a single paragraph.
def continue_entry_body entry_body, continuation
return unless last = entry_body.last
@ -21,6 +38,9 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
end
end
##
# Creates an RDoc::Markup::Document given the +groups+ of ChangeLog entries.
def create_document groups
doc = RDoc::Markup::Document.new
doc.omit_headings_below = 2
@ -39,6 +59,10 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
doc
end
##
# Returns a list of ChangeLog entries an RDoc::Markup nodes for the given
# +entries+.
def create_entries entries
out = []
@ -52,6 +76,10 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
out
end
##
# Returns an RDoc::Markup::List containing the given +items+ in the
# ChangeLog
def create_items items
list = RDoc::Markup::List.new :NOTE
@ -69,12 +97,30 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
list
end
##
# Groups +entries+ by date.
def group_entries entries
entries.group_by do |title, body|
entries.group_by do |title, _|
Time.parse(title).strftime "%Y-%m-%d"
end
end
##
# Parses the entries in the ChangeLog.
#
# Returns an Array of each ChangeLog entry in order of parsing.
#
# A ChangeLog entry is an Array containing the ChangeLog title (date and
# committer) and an Array of ChangeLog items (file and function changed with
# description).
#
# An example result would be:
#
# [ 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
# [ 'README.EXT: Converted to RDoc format',
# 'README.EXT.ja: ditto']]
def parse_entries
entries = []
entry_name = nil
@ -122,6 +168,9 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
entries
end
##
# Converts the ChangeLog into an RDoc::Markup::Document
def scan
entries = parse_entries
grouped_entries = group_entries entries

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

@ -240,7 +240,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# with :: separated named) and return the ultimate name, the associated
# container, and the given name (with the ::).
def get_class_or_module container
def get_class_or_module container, ignore_constants = false
skip_tkspace
name_t = get_tk
given_name = ''
@ -259,9 +259,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
while TkCOLON2 === peek_tk do
prev_container = container
container = container.find_module_named name_t.name
container ||= prev_container.add_module RDoc::NormalModule, name_t.name
container ||=
if ignore_constants then
RDoc::Context.new
else
c = prev_container.add_module RDoc::NormalModule, name_t.name
c.ignore unless prev_container.document_children
c
end
container.ignore unless prev_container.document_children
container.record_location @top_level
get_tk
skip_tkspace false
@ -663,9 +670,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
##
# Parses a constant in +context+ with +comment+
# Parses a constant in +context+ with +comment+. If +ignore_constants+ is
# true, no found constants will be added to RDoc.
def parse_constant container, tk, comment
def parse_constant container, tk, comment, ignore_constants = false
offset = tk.seek
line_no = tk.line_no
@ -676,6 +684,17 @@ class RDoc::Parser::Ruby < RDoc::Parser
eq_tk = get_tk
if TkCOLON2 === eq_tk then
unget_tk eq_tk
unget_tk tk
container, name_t, = get_class_or_module container, ignore_constants
name = name_t.name
eq_tk = get_tk
end
unless TkASSIGN === eq_tk then
unget_tk eq_tk
return false
@ -1334,6 +1353,26 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
##
# Parses a rescue
def parse_rescue
skip_tkspace false
while tk = get_tk
case tk
when TkNL, TkSEMICOLON then
break
when TkCOMMA then
skip_tkspace false
get_tk if TkNL === peek_tk
end
skip_tkspace false
end
end
##
# The core of the ruby parser.
@ -1407,7 +1446,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
parse_method container, single, tk, comment
when TkCONSTANT then
unless parse_constant container, tk, comment then
unless parse_constant container, tk, comment, current_method then
try_parse_comment = true
end
@ -1441,6 +1480,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
when TkSUPER then
current_method.calls_super = true if current_method
when TkRESCUE then
parse_rescue
when TkIDENTIFIER then
if nest == 1 and current_method.nil? then
case tk.name

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

@ -2,21 +2,60 @@ require 'rdoc'
require 'time'
require 'webrick'
##
# This is a WEBrick servlet that allows you to browse ri documentation.
#
# You can show documentation through either `ri --server` or, with RubyGems
# 2.0 or newer, `gem server`. For ri, the server runs on port 8214 by
# default. For RubyGems the server runs on port 8808 by default.
#
# You can use this servlet in your own project by mounting it on a WEBrick
# server:
#
# require 'webrick'
#
# server = WEBrick::HTTPServer.new Port: 8000
#
# server.mount '/', RDoc::Servlet
#
# If you want to mount the servlet some other place than the root, provide the
# base path when mounting:
#
# server.mount '/rdoc', RDoc::Servlet, '/rdoc'
class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
@server_stores = Hash.new { |hash, server| hash[server] = {} }
@cache = Hash.new { |hash, store| hash[store] = {} }
##
# Maps an asset type to its path on the filesystem
attr_reader :asset_dirs
##
# An RDoc::Options instance used for rendering options
attr_reader :options
def self.get_instance server, *options
##
# Creates an instance of this servlet that shares cached data between
# requests.
def self.get_instance server, *options # :nodoc:
stores = @server_stores[server]
new server, stores, @cache, *options
end
##
# Creates a new WEBrick servlet.
#
# Use +mount_path+ when mounting the servlet somewhere other than /.
#
# +server+ is provided automatically by WEBrick when mounting. +stores+ and
# +cache+ are provided automatically by the servlet.
def initialize server, stores, cache, mount_path = nil
super server
@ -44,6 +83,9 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
}
end
##
# Serves the asset at the path in +req+ for +generator_name+ via +res+.
def asset generator_name, req, res
asset_dir = @asset_dirs[generator_name]
@ -60,6 +102,9 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
end
end
##
# GET request entry point. Fills in +res+ for the path, etc. in +req+.
def do_GET req, res
req.path.sub!(/^#{Regexp.escape @mount_path}/o, '') if @mount_path
@ -82,6 +127,13 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
error e, req, res
end
##
# Fills in +res+ with the class, module or page for +req+ from +store+.
#
# +path+ is relative to the mount_path and is used to determine the class,
# module or page name (/RDoc/Servlet.html becomes RDoc::Servlet).
# +generator+ is used to create the page.
def documentation_page store, generator, path, req, res
name = path.sub(/.html$/, '').gsub '/', '::'
@ -94,6 +146,10 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
end
end
##
# Creates the JSON search index on +res+ for the given +store+. +generator+
# must respond to \#json_index to build. +req+ is ignored.
def documentation_search store, generator, req, res
json_index = @cache[store].fetch :json_index do
@cache[store][:json_index] =
@ -104,6 +160,10 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
res.body = "var search_data = #{json_index}"
end
##
# Returns the RDoc::Store and path relative to +mount_path+ for
# documentation at +path+.
def documentation_source path
_, source_name, path = path.split '/', 3
@ -119,8 +179,11 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
return store, path
end
def error e, req, res
backtrace = e.backtrace.join "\n"
##
# Generates an error page for the +exception+ while handling +req+ on +res+.
def error exception, req, res
backtrace = exception.backtrace.join "\n"
res.content_type = 'text/html'
res.status = 500
@ -130,7 +193,7 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<title>Error - #{ERB::Util.html_escape e.class}</title>
<title>Error - #{ERB::Util.html_escape exception.class}</title>
<link type="text/css" media="screen" href="#{@mount_path}/rdoc.css" rel="stylesheet">
</head>
@ -138,10 +201,17 @@ class RDoc::Servlet < WEBrick::HTTPServlet::AbstractServlet
<h1>Error</h1>
<p>While processing <code>#{ERB::Util.html_escape req.request_uri}</code> the
RDoc server has encountered a <code>#{ERB::Util.html_escape e.class}</code>
RDoc (#{ERB::Util.html_escape RDoc::VERSION}) server has encountered a
<code>#{ERB::Util.html_escape exception.class}</code>
exception:
<pre>#{ERB::Util.html_escape e.message}</pre>
<pre>#{ERB::Util.html_escape exception.message}</pre>
<p>Please report this to the
<a href="https://github.com/rdoc/rdoc/issues">RDoc issues tracker</a>. Please
include the RDoc version, the URI above and exception class, message and
backtrace. If you're viewing a gem's documentation, include the gem name and
version. If you're viewing Ruby's documentation, include the version of ruby.
<p>Backtrace:
@ -152,6 +222,9 @@ exception:
BODY
end
##
# Instantiates a Darkfish generator for +store+
def generator_for store
generator = RDoc::Generator::Darkfish.new store, @options
generator.file_output = false
@ -168,6 +241,11 @@ exception:
generator
end
##
# Handles the If-Modified-Since HTTP header on +req+ for +path+. If the
# file has not been modified a Not Modified response is returned. If the
# file has been modified a Last-Modified header is added to +res+.
def if_modified_since req, res, path = nil
last_modified = File.stat(path).mtime if path
@ -183,6 +261,14 @@ exception:
end
end
##
# Returns an Array of installed documentation.
#
# Each entry contains the documentation name (gem name, 'Ruby
# Documentation', etc.), the path relative to the mount point, whether the
# documentation exists, the type of documentation (See RDoc::RI::Paths#each)
# and the filesystem to the RDoc::Store for the documentation.
def installed_docs
ri_paths.map do |path, type|
store = RDoc::Store.new path, type
@ -202,15 +288,24 @@ exception:
end
end
##
# Returns a 404 page built by +generator+ for +req+ on +res+.
def not_found generator, req, res
res.body = generator.generate_servlet_not_found req.path
res.status = 404
end
##
# Enumerates the ri paths. See RDoc::RI::Paths#each
def ri_paths &block
RDoc::RI::Paths.each true, true, true, :all, &block
end
##
# Generates the root page on +res+. +req+ is ignored.
def root req, res
generator = RDoc::Generator::Darkfish.new nil, @options
@ -219,6 +314,9 @@ exception:
res.content_type = 'text/html'
end
##
# Generates a search index for the root page on +res+. +req+ is ignored.
def root_search req, res
search_index = []
info = []
@ -259,6 +357,10 @@ exception:
res.content_type = 'application/javascript'
end
##
# Displays documentation for +req+ on +res+, whether that be HTML or some
# asset.
def show_documentation req, res
store, path = documentation_source req.path
@ -280,6 +382,9 @@ exception:
res.content_type ||= 'text/html'
end
##
# Returns an RDoc::Store for the given +source_name+ ('ruby' or a gem name).
def store_for source_name
case source_name
when 'ruby' then

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

@ -59,7 +59,7 @@ class RDoc::Store
@name = name
end
def message
def message # :nodoc:
"store at #{@store.path} missing file #{@file} for #{@name}"
end

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

@ -70,7 +70,7 @@ call-seq:
comment = RDoc::Comment.new <<-COMMENT, @top_level
# call-seq:
# bla => true or false
#\s
#
# moar comment
COMMENT

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

@ -80,23 +80,53 @@ class C; end
assert_equal 'A', name_t.text
assert_equal 'A', given_name
cont, name_t, given_name = util_parser('A::B') .get_class_or_module ctxt
cont, name_t, given_name = util_parser('B::C') .get_class_or_module ctxt
assert_equal @store.find_module_named('A'), cont
assert_equal 'B', name_t.text
assert_equal 'A::B', given_name
b = @store.find_module_named('B')
assert_equal b, cont
assert_equal [@top_level], b.in_files
assert_equal 'C', name_t.text
assert_equal 'B::C', given_name
cont, name_t, given_name = util_parser('A:: B').get_class_or_module ctxt
cont, name_t, given_name = util_parser('D:: E').get_class_or_module ctxt
assert_equal @store.find_module_named('A'), cont
assert_equal 'B', name_t.text
assert_equal 'A::B', given_name
assert_equal @store.find_module_named('D'), cont
assert_equal 'E', name_t.text
assert_equal 'D::E', given_name
assert_raises NoMethodError do
util_parser("A::\nB").get_class_or_module ctxt
end
end
def test_get_class_or_module_document_children
ctxt = @top_level.add_class RDoc::NormalClass, 'A'
ctxt.stop_doc
util_parser('B::C').get_class_or_module ctxt
b = @store.find_module_named('A::B')
assert b.ignored?
d = @top_level.add_class RDoc::NormalClass, 'A::D'
util_parser('D::E').get_class_or_module ctxt
refute d.ignored?
end
def test_get_class_or_module_ignore_constants
ctxt = RDoc::Context.new
ctxt.store = @store
util_parser('A') .get_class_or_module ctxt, true
util_parser('A::B').get_class_or_module ctxt, true
assert_empty ctxt.constants
assert_empty @store.modules_hash.keys
assert_empty @store.classes_hash.keys
end
def test_get_class_specification
assert_equal 'A', util_parser('A') .get_class_specification
assert_equal 'A::B', util_parser('A::B').get_class_specification
@ -1108,6 +1138,37 @@ EOF
assert_equal 'A', bar.find_module_named('A').full_name
end
def test_parse_constant_in_method
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser 'A::B = v'
tk = @parser.get_tk
@parser.parse_constant klass, tk, @comment, true
assert_empty klass.constants
assert_empty @store.modules_hash.keys
assert_equal %w[Foo], @store.classes_hash.keys
end
def test_parse_constant_rescue
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
util_parser "A => e"
tk = @parser.get_tk
@parser.parse_constant klass, tk, @comment
assert_empty klass.constants
assert_empty klass.modules
assert_empty @store.modules_hash.keys
assert_equal %w[Foo], @store.classes_hash.keys
end
def test_parse_constant_stopdoc
klass = @top_level.add_class RDoc::NormalClass, 'Foo'
klass.stop_doc
@ -1121,6 +1182,27 @@ EOF
assert_empty klass.constants
end
def test_parse_comment_nested
content = <<-CONTENT
A::B::C = 1
CONTENT
util_parser content
tk = @parser.get_tk
parsed = @parser.parse_constant @top_level, tk, 'comment'
assert parsed
a = @top_level.find_module_named 'A'
b = a.find_module_named 'B'
c = b.constants.first
assert_equal 'A::B::C', c.full_name
assert_equal 'comment', c.comment
end
def test_parse_include
klass = RDoc::NormalClass.new 'C'
klass.parent = @top_level
@ -2585,6 +2667,61 @@ end
assert_equal 'A nice girl', m.comment.text
end
def test_scan_constant_in_method
content = <<-CONTENT # newline is after M is important
module M
def m
A
B::C
end
end
CONTENT
util_parser content
@parser.scan
m = @top_level.modules.first
assert_empty m.constants
assert_empty @store.classes_hash.keys
assert_equal %w[M], @store.modules_hash.keys
end
def test_scan_constant_in_rescue
content = <<-CONTENT # newline is after M is important
module M
def m
rescue A::B
rescue A::C => e
rescue A::D, A::E
rescue A::F,
A::G
rescue H
rescue I => e
rescue J, K
rescue L =>
e
rescue M;
rescue N,
O => e
end
end
CONTENT
util_parser content
@parser.scan
m = @top_level.modules.first
assert_empty m.constants
assert_empty @store.classes_hash.keys
assert_equal %w[M], @store.modules_hash.keys
end
def test_scan_constant_nodoc
content = <<-CONTENT # newline is after M is important
module M