1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
require 'time'
##
# A ChangeLog file parser.
#
# This parser converts a ChangeLog into an RDoc::Markup::Document. When
# viewed as HTML a ChangeLog page will have an entry for each day's entries in
# the sidebar table of contents.
#
# This parser is meant to parse the MRI ChangeLog, but can be used to parse any
# {GNU style Change
# Log}[http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html].
class RDoc::Parser::ChangeLog < RDoc::Parser
include RDoc::Parser::Text
parse_files_matching(/(\/|\\|\A)ChangeLog[^\/\\]*\z/)
##
# Attaches the +continuation+ of the previous line to the +entry_body+.
#
# Continued function listings are joined together as a single entry.
# Continued descriptions are joined to make a single paragraph.
def continue_entry_body entry_body, continuation
return unless last = entry_body.last
if last =~ /\)\s*\z/ and continuation =~ /\A\(/ then
last.sub!(/\)\s*\z/, ',')
continuation.sub!(/\A\(/, '')
end
if last =~ /\s\z/ then
last << continuation
else
last << ' ' << continuation
end
end
##
# Creates an RDoc::Markup::Document given the +groups+ of ChangeLog entries.
def create_document groups
doc = RDoc::Markup::Document.new
doc.omit_headings_below = 2
doc.file = @top_level
doc << RDoc::Markup::Heading.new(1, File.basename(@file_name))
doc << RDoc::Markup::BlankLine.new
groups.sort_by do |day,| day end.reverse_each do |day, entries|
doc << RDoc::Markup::Heading.new(2, day.dup)
doc << RDoc::Markup::BlankLine.new
doc.concat create_entries entries
end
doc
end
##
# Returns a list of ChangeLog entries an RDoc::Markup nodes for the given
# +entries+.
def create_entries entries
out = []
entries.each do |entry, items|
out << RDoc::Markup::Heading.new(3, entry)
out << RDoc::Markup::BlankLine.new
out << create_items(items)
end
out
end
##
# Returns an RDoc::Markup::List containing the given +items+ in the
# ChangeLog
def create_items items
list = RDoc::Markup::List.new :NOTE
items.each do |item|
item =~ /\A(.*?(?:\([^)]+\))?):\s*/
title = $1
body = $'
paragraph = RDoc::Markup::Paragraph.new body
list_item = RDoc::Markup::ListItem.new title, paragraph
list << list_item
end
list
end
##
# Groups +entries+ by date.
def group_entries entries
entries.group_by do |title, _|
begin
Time.parse(title).strftime '%Y-%m-%d'
rescue NoMethodError, ArgumentError
time, = title.split ' ', 2
Time.parse(time).strftime '%Y-%m-%d'
end
end
end
##
# Parses the entries in the ChangeLog.
#
# Returns an Array of each ChangeLog entry in order of parsing.
#
# A ChangeLog entry is an Array containing the ChangeLog title (date and
# committer) and an Array of ChangeLog items (file and function changed with
# description).
#
# An example result would be:
#
# [ 'Tue Dec 4 08:33:46 2012 Eric Hodel <drbrain@segment7.net>',
# [ 'README.EXT: Converted to RDoc format',
# 'README.EXT.ja: ditto']]
def parse_entries
entries = []
entry_name = nil
entry_body = []
@content.each_line do |line|
case line
when /^\s*$/ then
next
when /^\w.*/ then
entries << [entry_name, entry_body] if entry_name
entry_name = $&
begin
time = Time.parse entry_name
# HACK Ruby 1.8 does not raise ArgumentError for Time.parse "Other"
entry_name = nil unless entry_name =~ /#{time.year}/
rescue NoMethodError
time, = entry_name.split ' ', 2
time = Time.parse time
rescue ArgumentError
entry_name = nil
end
entry_body = []
when /^(\t| {8})?\*\s*(.*)/ then # "\t* file.c (func): ..."
entry_body << $2
when /^(\t| {8})?\s*(\(.*)/ then # "\t(func): ..."
entry = $2
if entry_body.last =~ /:/ then
entry_body << entry
else
continue_entry_body entry_body, entry
end
when /^(\t| {8})?\s*(.*)/ then
continue_entry_body entry_body, $2
end
end
entries << [entry_name, entry_body] if entry_name
entries.reject! do |(entry,_)|
entry == nil
end
entries
end
##
# Converts the ChangeLog into an RDoc::Markup::Document
def scan
entries = parse_entries
grouped_entries = group_entries entries
doc = create_document grouped_entries
@top_level.comment = doc
@top_level
end
end
|