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
258
259
|
# coding: utf-8
##
# For RDoc::Text#to_html
require 'strscan'
##
# Methods for manipulating comment text
module RDoc::Text
##
# Maps an encoding to a Hash of characters properly transcoded for that
# encoding.
#
# See also encode_fallback.
TO_HTML_CHARACTERS = Hash.new do |h, encoding|
h[encoding] = {
:close_dquote => encode_fallback('”', encoding, '"'),
:close_squote => encode_fallback('’', encoding, '\''),
:copyright => encode_fallback('©', encoding, '(c)'),
:ellipsis => encode_fallback('…', encoding, '...'),
:em_dash => encode_fallback('—', encoding, '---'),
:en_dash => encode_fallback('–', encoding, '--'),
:open_dquote => encode_fallback('“', encoding, '"'),
:open_squote => encode_fallback('‘', encoding, '\''),
:trademark => encode_fallback('®', encoding, '(r)'),
}
end if Object.const_defined? :Encoding
##
# Transcodes +character+ to +encoding+ with a +fallback+ character.
def self.encode_fallback character, encoding, fallback
character.encode(encoding, :fallback => { character => fallback },
:undef => :replace, :replace => fallback)
end
##
# Expands tab characters in +text+ to eight spaces
def expand_tabs text
expanded = []
text.each_line do |line|
line.gsub!(/^(.{8}*?)([^\t\r\n]{0,7})\t/) do
"#{$1}#{$2}#{' ' * (8 - $2.size)}"
end until line !~ /\t/
expanded << line
end
expanded.join
end
##
# Flush +text+ left based on the shortest line
def flush_left text
indents = []
text.each_line do |line|
indents << (line =~ /[^\s]/ || 9999)
end
indent = indents.min
flush = []
text.each_line do |line|
line[/^ {0,#{indent}}/] = ''
flush << line
end
flush.join
end
##
# Convert a string in markup format into HTML.
#
# Requires the including class to implement #formatter
def markup text
document = parse text
document.accept formatter
end
##
# Strips hashes, expands tabs then flushes +text+ to the left
def normalize_comment text
return text if text.empty?
text = strip_hashes text
text = expand_tabs text
text = flush_left text
strip_newlines text
end
##
# Normalizes +text+ then builds a RDoc::Markup::Document from it
def parse text
return text if RDoc::Markup::Document === text
text = normalize_comment text
return RDoc::Markup::Document.new if text =~ /\A\n*\z/
RDoc::Markup::Parser.parse text
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:
http://rubyforge.org/tracker/?atid=2472&group_id=627&func=browse
EOF
raise
end
##
# Strips leading # characters from +text+
def strip_hashes text
return text if text =~ /^(?>\s*)[^\#]/
text.gsub(/^\s*(#+)/) { $1.tr '#',' ' }.gsub(/^\s+$/, '')
end
##
# Strips leading and trailing \n characters from +text+
def strip_newlines text
text.gsub(/\A\n*(.*?)\n*\z/m, '\1')
end
##
# Strips /* */ style comments
def strip_stars text
text = text.gsub %r%Document-method:\s+[\w:.#]+%, ''
text.sub! %r%/\*+% do " " * $&.length end
text.sub! %r%\*+/% do " " * $&.length end
text.gsub! %r%^[ \t]*\*%m do " " * $&.length end
text.gsub(/^\s+$/, '')
end
##
# Converts ampersand, dashes, ellipsis, quotes, copyright and registered
# trademark symbols in +text+ to properly encoded characters.
def to_html text
if Object.const_defined? :Encoding then
html = ''.encode text.encoding
encoded = RDoc::Text::TO_HTML_CHARACTERS[text.encoding]
else
html = ''
encoded = {
:close_dquote => '”',
:close_squote => '’',
:copyright => '©',
:ellipsis => '…',
:em_dash => '—',
:en_dash => '–',
:open_dquote => '“',
:open_squote => '‘',
:trademark => '®',
}
end
s = StringScanner.new text
insquotes = false
indquotes = false
after_word = nil
until s.eos? do
case
when s.scan(/<tt>.*?<\/tt>/) then # skip contents of tt
html << s.matched.gsub('\\\\', '\\')
when s.scan(/<tt>.*?/) then
warn 'mismatched <tt> tag' # TODO signal file/line
html << s.matched
when s.scan(/<[^>]+\/?s*>/) then # skip HTML tags
html << s.matched
when s.scan(/\\(\S)/) then # unhandled suppressed crossref
html << s[1]
after_word = nil
when s.scan(/\.\.\.(\.?)/) then
html << s[1] << encoded[:ellipsis]
after_word = nil
when s.scan(/\(c\)/) then
html << encoded[:copyright]
after_word = nil
when s.scan(/\(r\)/) then
html << encoded[:trademark]
after_word = nil
when s.scan(/---/) then
html << encoded[:em_dash]
after_word = nil
when s.scan(/--/) then
html << encoded[:en_dash]
after_word = nil
when s.scan(/"|"/) then
html << encoded[indquotes ? :close_dquote : :open_dquote]
indquotes = !indquotes
after_word = nil
when s.scan(/``/) then # backtick double quote
html << encoded[:open_dquote]
after_word = nil
when s.scan(/''/) then # tick double quote
html << encoded[:close_dquote]
after_word = nil
when s.scan(/'/) then # single quote
if insquotes
html << encoded[:close_squote]
insquotes = false
elsif after_word
# Mary's dog, my parents' house: do not start paired quotes
html << encoded[:close_squote]
else
html << encoded[:open_squote]
insquotes = true
end
after_word = nil
else # advance to the next potentially significant character
match = s.scan(/.+?(?=[<\\.("'`&-])/) #"
if match then
html << match
after_word = match =~ /\w$/
else
html << s.rest
break
end
end
end
html
end
end
|