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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
# :markup: tomdoc
# A parser for TomDoc based on TomDoc 1.0.0-rc1 (02adef9b5a)
#
# The TomDoc specification can be found at:
#
# http://tomdoc.org
#
# The latest version of the TomDoc specification can be found at:
#
# https://github.com/mojombo/tomdoc/blob/master/tomdoc.md
#
# To choose TomDoc as your only default format see RDoc::Options@Saved+Options
# for instructions on setting up a <code>.rdoc_options</code> file to store
# your project default.
#
# There are a few differences between this parser and the specification. A
# best-effort was made to follow the specification as closely as possible but
# some choices to deviate were made.
#
# A future version of RDoc will warn when a MUST or MUST NOT is violated and
# may warn when a SHOULD or SHOULD NOT is violated. RDoc will always try
# to emit documentation even if given invalid TomDoc.
#
# Here are some implementation choices this parser currently makes:
#
# This parser allows rdoc-style inline markup but you should not depended on
# it.
#
# This parser allows a space between the comment and the method body.
#
# This parser does not require the default value to be described for an
# optional argument.
#
# This parser does not examine the order of sections. An Examples section may
# precede the Arguments section.
#
# This class is documented in TomDoc format. Since this is a subclass of the
# RDoc markup parser there isn't much to see here, unfortunately.
class RDoc::TomDoc < RDoc::Markup::Parser
# Internal: Token accessor
attr_reader :tokens
# Internal: Adds a post-processor which sets the RDoc section based on the
# comment's status.
#
# Returns nothing.
def self.add_post_processor # :nodoc:
RDoc::Markup::PreProcess.post_process do |comment, code_object|
next unless code_object and
RDoc::Comment === comment and comment.format == 'tomdoc'
comment.text.gsub!(/(\A\s*# )(Public|Internal|Deprecated):\s+/) do
section = code_object.add_section $2
code_object.temporary_section = section
$1
end
end
end
add_post_processor
# Public: Parses TomDoc from text
#
# text - A String containing TomDoc-format text.
#
# Examples
#
# RDoc::TomDoc.parse <<-TOMDOC
# This method does some things
#
# Returns nothing.
# TOMDOC
# # => #<RDoc::Markup::Document:0xXXX @parts=[...], @file=nil>
#
# Returns an RDoc::Markup::Document representing the TomDoc format.
def self.parse text
parser = new
parser.tokenize text
doc = RDoc::Markup::Document.new
parser.parse doc
doc
end
# Internal: Extracts the Signature section's method signature
#
# comment - An RDoc::Comment that will be parsed and have the signature
# extracted
#
# Returns a String containing the signature and nil if not
def self.signature comment
return unless comment.tomdoc?
document = comment.parse
signature = nil
found_heading = false
found_signature = false
document.parts.delete_if do |part|
next false if found_signature
found_heading ||=
RDoc::Markup::Heading === part && part.text == 'Signature'
next false unless found_heading
next true if RDoc::Markup::BlankLine === part
if RDoc::Markup::Verbatim === part then
signature = part
found_signature = true
end
end
signature and signature.text
end
# Public: Creates a new TomDoc parser. See also RDoc::Markup::parse
def initialize
super
@section = nil
@seen_returns = false
end
# Internal: Builds a heading from the token stream
#
# level - The level of heading to create
#
# Returns an RDoc::Markup::Heading
def build_heading level
heading = super
@section = heading.text
heading
end
# Internal: Builds a verbatim from the token stream. A verbatim in the
# Examples section will be marked as in Ruby format.
#
# margin - The indentation from the margin for lines that belong to this
# verbatim section.
#
# Returns an RDoc::Markup::Verbatim
def build_verbatim margin
verbatim = super
verbatim.format = :ruby if @section == 'Examples'
verbatim
end
# Internal: Builds a paragraph from the token stream
#
# margin - Unused
#
# Returns an RDoc::Markup::Paragraph.
def build_paragraph margin
p :paragraph_start => margin if @debug
paragraph = RDoc::Markup::Paragraph.new
until @tokens.empty? do
type, data, = get
case type
when :TEXT then
@section = 'Returns' if data =~ /\AReturns/
paragraph << data
when :NEWLINE then
if :TEXT == peek_token[0] then
paragraph << ' '
else
break
end
else
unget
break
end
end
p :paragraph_end => margin if @debug
paragraph
end
##
# Detects a section change to "Returns" and adds a heading
def parse_text parent, indent # :nodoc:
paragraph = build_paragraph indent
if false == @seen_returns and 'Returns' == @section then
@seen_returns = true
parent << RDoc::Markup::Heading.new(3, 'Returns')
parent << RDoc::Markup::BlankLine.new
end
parent << paragraph
end
# Internal: Turns text into an Array of tokens
#
# text - A String containing TomDoc-format text.
#
# Returns self.
def tokenize text
text.sub!(/\A(Public|Internal|Deprecated):\s+/, '')
setup_scanner text
until @s.eos? do
pos = @s.pos
# leading spaces will be reflected by the column of the next token
# the only thing we loose are trailing spaces at the end of the file
next if @s.scan(/ +/)
@tokens << case
when @s.scan(/\r?\n/) then
token = [:NEWLINE, @s.matched, *token_pos(pos)]
@line_pos = char_pos @s.pos
@line += 1
token
when @s.scan(/(Examples|Signature)$/) then
@tokens << [:HEADER, 3, *token_pos(pos)]
[:TEXT, @s[1], *token_pos(pos)]
when @s.scan(/([:\w][\w\[\]]*)[ ]+- /) then
[:NOTE, @s[1], *token_pos(pos)]
else
@s.scan(/.*/)
[:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)]
end
end
self
end
end
|