rdoc 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +1 -3
- data/RI.md +75 -75
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +71 -69
- data/lib/rdoc/code_object/any_method.rb +305 -303
- data/lib/rdoc/code_object/attr.rb +150 -148
- data/lib/rdoc/code_object/class_module.rb +798 -792
- data/lib/rdoc/code_object/constant.rb +175 -173
- data/lib/rdoc/code_object/context/section.rb +142 -138
- data/lib/rdoc/code_object/context.rb +926 -958
- data/lib/rdoc/code_object/extend.rb +7 -5
- data/lib/rdoc/code_object/include.rb +7 -5
- data/lib/rdoc/code_object/method_attr.rb +326 -319
- data/lib/rdoc/code_object/mixin.rb +97 -95
- data/lib/rdoc/code_object/normal_class.rb +77 -78
- data/lib/rdoc/code_object/normal_module.rb +61 -59
- data/lib/rdoc/code_object/require.rb +23 -39
- data/lib/rdoc/code_object/single_class.rb +21 -19
- data/lib/rdoc/code_object/top_level.rb +212 -219
- data/lib/rdoc/code_object.rb +305 -303
- data/lib/rdoc/comment.rb +275 -273
- data/lib/rdoc/cross_reference.rb +192 -190
- data/lib/rdoc/encoding.rb +105 -103
- data/lib/rdoc/erb_partial.rb +13 -11
- data/lib/rdoc/erbio.rb +29 -27
- data/lib/rdoc/generator/aliki.rb +161 -153
- data/lib/rdoc/generator/darkfish.rb +645 -635
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +164 -146
- data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
- data/lib/rdoc/generator/pot/po.rb +52 -51
- data/lib/rdoc/generator/pot/po_entry.rb +138 -132
- data/lib/rdoc/generator/pot.rb +85 -81
- data/lib/rdoc/generator/ri.rb +23 -19
- data/lib/rdoc/generator/template/aliki/DESIGN.md +6 -4
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +10 -10
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +207 -178
- data/lib/rdoc/generator/template/aliki/js/aliki.js +60 -84
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator.rb +48 -46
- data/lib/rdoc/i18n/locale.rb +99 -95
- data/lib/rdoc/i18n/text.rb +109 -105
- data/lib/rdoc/i18n.rb +7 -5
- data/lib/rdoc/markdown/byte_runtime.rb +80 -0
- data/lib/rdoc/markdown.kpeg +15 -11
- data/lib/rdoc/markdown.rb +40 -47
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +219 -215
- data/lib/rdoc/markup/include.rb +33 -29
- data/lib/rdoc/markup/indented_paragraph.rb +37 -33
- data/lib/rdoc/markup/inline_parser.rb +281 -277
- data/lib/rdoc/markup/list.rb +80 -88
- data/lib/rdoc/markup/list_item.rb +73 -85
- data/lib/rdoc/markup/paragraph.rb +23 -19
- data/lib/rdoc/markup/parser.rb +501 -497
- data/lib/rdoc/markup/pre_process.rb +283 -279
- data/lib/rdoc/markup/raw.rb +2 -2
- data/lib/rdoc/markup/rule.rb +16 -12
- data/lib/rdoc/markup/to_ansi.rb +143 -139
- data/lib/rdoc/markup/to_bs.rb +72 -68
- data/lib/rdoc/markup/to_html.rb +594 -565
- data/lib/rdoc/markup/to_html_crossref.rb +234 -230
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +36 -32
- data/lib/rdoc/markup/to_label.rb +63 -59
- data/lib/rdoc/markup/to_markdown.rb +212 -208
- data/lib/rdoc/markup/to_rdoc.rb +336 -332
- data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
- data/lib/rdoc/markup/to_test.rb +60 -56
- data/lib/rdoc/markup/to_tt_only.rb +84 -80
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1061
- data/lib/rdoc/parser/c.rb +1039 -1037
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +239 -235
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1245 -1124
- data/lib/rdoc/parser/ruby_colorizer.rb +263 -213
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +228 -220
- data/lib/rdoc/rbs_helper.rb +1 -1
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +500 -491
- data/lib/rdoc/ri/driver.rb +1140 -1135
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +354 -350
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -12
- data/lib/rdoc/rubygems_hook.rb +183 -181
- data/lib/rdoc/server.rb +349 -347
- data/lib/rdoc/stats/normal.rb +46 -42
- data/lib/rdoc/stats/quiet.rb +39 -35
- data/lib/rdoc/stats/verbose.rb +35 -31
- data/lib/rdoc/stats.rb +365 -363
- data/lib/rdoc/store.rb +888 -902
- data/lib/rdoc/task.rb +260 -256
- data/lib/rdoc/text.rb +135 -133
- data/lib/rdoc/token_stream.rb +101 -93
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- metadata +4 -5
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
data/lib/rdoc/markup/to_html.rb
CHANGED
|
@@ -3,680 +3,709 @@ require 'cgi/escape'
|
|
|
3
3
|
require 'cgi/util' unless defined?(CGI::EscapeExt)
|
|
4
4
|
require 'prism'
|
|
5
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
|
-
'(c)' => :copyright,
|
|
66
|
-
'(C)' => :copyright,
|
|
67
|
-
'(r)' => :trademark,
|
|
68
|
-
'(R)' => :trademark,
|
|
69
|
-
'---' => :em_dash,
|
|
70
|
-
'--' => :en_dash,
|
|
71
|
-
'....' => :dot_ellipsis,
|
|
72
|
-
'...' => :ellipsis,
|
|
73
|
-
'``' => :open_dquote,
|
|
74
|
-
"''" => :close_dquote,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
# Transcodes +character+ to +encoding+ with a +fallback+ character.
|
|
78
|
-
|
|
79
|
-
def self.encode_fallback(character, encoding, fallback)
|
|
80
|
-
character.encode(
|
|
81
|
-
encoding,
|
|
82
|
-
fallback: { character => fallback },
|
|
83
|
-
undef: :replace,
|
|
84
|
-
replace: fallback
|
|
85
|
-
)
|
|
86
|
-
end
|
|
6
|
+
module RDoc
|
|
7
|
+
class Markup
|
|
8
|
+
##
|
|
9
|
+
# Outputs RDoc markup as HTML.
|
|
10
|
+
|
|
11
|
+
class ToHtml < Markup::Formatter
|
|
12
|
+
|
|
13
|
+
include Text
|
|
14
|
+
|
|
15
|
+
# :section: Utilities
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
|
|
19
|
+
|
|
20
|
+
LIST_TYPE_TO_HTML = {
|
|
21
|
+
:BULLET => ['<ul>', '</ul>'],
|
|
22
|
+
:LABEL => ['<dl class="rdoc-list label-list">', '</dl>'],
|
|
23
|
+
:LALPHA => ['<ol style="list-style-type: lower-alpha">', '</ol>'],
|
|
24
|
+
:NOTE => ['<dl class="rdoc-list note-list">', '</dl>'],
|
|
25
|
+
:NUMBER => ['<ol>', '</ol>'],
|
|
26
|
+
:UALPHA => ['<ol style="list-style-type: upper-alpha">', '</ol>'],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
attr_reader :res # :nodoc:
|
|
30
|
+
attr_reader :in_list_entry # :nodoc:
|
|
31
|
+
attr_reader :list # :nodoc:
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# The RDoc::CodeObject HTML is being generated for. This is used to
|
|
35
|
+
# generate namespaced URI fragments
|
|
36
|
+
|
|
37
|
+
attr_accessor :code_object
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Path to this document for relative links
|
|
41
|
+
|
|
42
|
+
attr_accessor :from_path
|
|
43
|
+
|
|
44
|
+
# :section:
|
|
45
|
+
|
|
46
|
+
# Maps an encoding to a Hash of characters properly transcoded for that
|
|
47
|
+
# encoding.
|
|
48
|
+
#
|
|
49
|
+
# See also encode_fallback.
|
|
50
|
+
|
|
51
|
+
TO_HTML_CHARACTERS = Hash.new do |h, encoding|
|
|
52
|
+
h[encoding] = {
|
|
53
|
+
close_dquote: encode_fallback('”', encoding, '"'),
|
|
54
|
+
close_squote: encode_fallback('’', encoding, '\''),
|
|
55
|
+
copyright: encode_fallback('©', encoding, '(c)'),
|
|
56
|
+
ellipsis: encode_fallback('…', encoding, '...'),
|
|
57
|
+
dot_ellipsis: encode_fallback('.…', encoding, '....'),
|
|
58
|
+
em_dash: encode_fallback('—', encoding, '---'),
|
|
59
|
+
en_dash: encode_fallback('–', encoding, '--'),
|
|
60
|
+
open_dquote: encode_fallback('“', encoding, '"'),
|
|
61
|
+
open_squote: encode_fallback('‘', encoding, '\''),
|
|
62
|
+
trademark: encode_fallback('®', encoding, '(r)'),
|
|
63
|
+
}
|
|
64
|
+
end
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
66
|
+
HTML_CHARACTER_ALIASES = {
|
|
67
|
+
'(c)' => :copyright,
|
|
68
|
+
'(C)' => :copyright,
|
|
69
|
+
'(r)' => :trademark,
|
|
70
|
+
'(R)' => :trademark,
|
|
71
|
+
'---' => :em_dash,
|
|
72
|
+
'--' => :en_dash,
|
|
73
|
+
'....' => :dot_ellipsis,
|
|
74
|
+
'...' => :ellipsis,
|
|
75
|
+
'``' => :open_dquote,
|
|
76
|
+
"''" => :close_dquote,
|
|
77
|
+
}
|
|
78
|
+
HTML_CHARACTERS_REGEXP = Regexp.union(HTML_CHARACTER_ALIASES.keys)
|
|
79
|
+
|
|
80
|
+
# Transcodes +character+ to +encoding+ with a +fallback+ character.
|
|
81
|
+
|
|
82
|
+
def self.encode_fallback(character, encoding, fallback)
|
|
83
|
+
character.encode(
|
|
84
|
+
encoding,
|
|
85
|
+
fallback: { character => fallback },
|
|
86
|
+
undef: :replace,
|
|
87
|
+
replace: fallback
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@in_squote = false
|
|
94
|
-
end
|
|
91
|
+
# Converts ascii quote pairs to multibyte quote characters
|
|
92
|
+
class QuoteConverter
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
when '"'
|
|
99
|
-
type = @in_dquote ? :close_dquote : :open_dquote
|
|
100
|
-
@in_dquote = !@in_dquote
|
|
101
|
-
when "'"
|
|
102
|
-
if @in_squote
|
|
103
|
-
type = :close_squote
|
|
94
|
+
def initialize
|
|
95
|
+
@in_dquote = false
|
|
104
96
|
@in_squote = false
|
|
105
|
-
elsif after_word
|
|
106
|
-
# Mary's dog, my parents' house: do not start paired quotes
|
|
107
|
-
type = :close_squote
|
|
108
|
-
else
|
|
109
|
-
type = :open_squote
|
|
110
|
-
@in_squote = true
|
|
111
97
|
end
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
|
|
99
|
+
def convert(quote, after_word:)
|
|
100
|
+
case quote
|
|
101
|
+
when '"'
|
|
102
|
+
type = @in_dquote ? :close_dquote : :open_dquote
|
|
103
|
+
@in_dquote = !@in_dquote
|
|
104
|
+
when "'"
|
|
105
|
+
if @in_squote
|
|
106
|
+
type = :close_squote
|
|
107
|
+
@in_squote = false
|
|
108
|
+
elsif after_word
|
|
109
|
+
# Mary's dog, my parents' house: do not start paired quotes
|
|
110
|
+
type = :close_squote
|
|
111
|
+
else
|
|
112
|
+
type = :open_squote
|
|
113
|
+
@in_squote = true
|
|
114
|
+
end
|
|
115
|
+
when '`'
|
|
116
|
+
# Opening quote of <tt>`quoted sentence'</tt>.
|
|
117
|
+
# This will conflict with code blocks <tt>`puts('hello')`</tt> in the future.
|
|
118
|
+
if !@in_squote && !after_word
|
|
119
|
+
type = :open_squote
|
|
120
|
+
@in_squote = true
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
TO_HTML_CHARACTERS[quote.encoding][type] if type
|
|
118
124
|
end
|
|
119
125
|
end
|
|
120
|
-
TO_HTML_CHARACTERS[quote.encoding][type] if type
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
##
|
|
128
|
+
# Creates a new formatter that will output HTML
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
def initialize(pipe: false, output_decoration: true)
|
|
131
|
+
super()
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
init_regexp_handlings
|
|
142
|
-
end
|
|
133
|
+
@pipe = pipe
|
|
134
|
+
@output_decoration = output_decoration
|
|
135
|
+
@code_object = nil
|
|
136
|
+
@from_path = ''
|
|
137
|
+
@in_list_entry = nil
|
|
138
|
+
@list = nil
|
|
139
|
+
@th = nil
|
|
140
|
+
@quote_converter = nil
|
|
141
|
+
@in_tidylink_label = false
|
|
142
|
+
@hard_break = "<br>\n"
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
144
|
+
init_regexp_handlings
|
|
145
|
+
end
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
# :section: Regexp Handling
|
|
148
|
+
#
|
|
149
|
+
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
##
|
|
152
|
+
# URL schemes that are not linked because they can run scripts or open
|
|
153
|
+
# local files. Links to them are rendered as their text only.
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
# external links
|
|
156
|
-
@markup.add_regexp_handling(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)#{URL_CHARACTERS_REGEXP_STR}+\w/,
|
|
157
|
-
:HYPERLINK)
|
|
155
|
+
UNSAFE_URL_SCHEME_REGEXP = /\A(?:javascript|vbscript|data|file):/i
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
# :nodoc:
|
|
158
|
+
URL_CHARACTERS_REGEXP_STR = /[A-Za-z0-9\-._~:\/\?#\[\]@!$&'\(\)*+,;%=]/.source
|
|
161
159
|
|
|
162
|
-
|
|
160
|
+
##
|
|
161
|
+
# Adds regexp handlings.
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
def init_regexp_handlings
|
|
164
|
+
# external links
|
|
165
|
+
@markup.add_regexp_handling(/(?:link:|https?:|mailto:|ftp:|irc:|www\.)#{URL_CHARACTERS_REGEXP_STR}+\w/o,
|
|
166
|
+
:HYPERLINK)
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
# suppress crossref: \#method \::method \ClassName \method_with_underscores
|
|
169
|
+
@markup.add_regexp_handling(/\\(?:[#:A-Z]|[a-z]+_[a-z0-9])/, :SUPPRESSED_CROSSREF)
|
|
169
170
|
|
|
170
|
-
|
|
171
|
-
# Adds regexp handlings about link notations.
|
|
171
|
+
@markup.add_regexp_handling(HTML_CHARACTERS_REGEXP, :HTML_CHARACTERS)
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
end
|
|
173
|
+
@markup.add_regexp_handling(/\b['"`]/, :QUOTE_AFTER_WORD)
|
|
174
|
+
@markup.add_regexp_handling(/\B['"`]/, :QUOTE_NOT_AFTER_WORD)
|
|
176
175
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
when /^rdoc-ref:/
|
|
180
|
-
CGI.escapeHTML($')
|
|
181
|
-
when /^rdoc-label:/
|
|
182
|
-
return CGI.escapeHTML(url) if in_tidylink_label?
|
|
183
|
-
text = $'
|
|
184
|
-
|
|
185
|
-
text = case text
|
|
186
|
-
when /\Alabel-/ then $'
|
|
187
|
-
when /\Afootmark-/ then $'
|
|
188
|
-
when /\Afoottext-/ then $'
|
|
189
|
-
else text
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
|
|
193
|
-
when /^rdoc-image:/
|
|
194
|
-
# Split the string after "rdoc-image:" into url and alt.
|
|
195
|
-
# "path/to/image.jpg:alt text" => ["path/to/image.jpg", "alt text"]
|
|
196
|
-
# "http://example.com/path/to/image.jpg:alt text" => ["http://example.com/path/to/image.jpg", "alt text"]
|
|
197
|
-
url, alt = $'.split(/:(?!\/)/, 2)
|
|
198
|
-
if alt && !alt.empty?
|
|
199
|
-
%[<img src="#{CGI.escapeHTML(url)}" alt="#{CGI.escapeHTML(alt)}">]
|
|
200
|
-
else
|
|
201
|
-
%[<img src="#{CGI.escapeHTML(url)}">]
|
|
202
|
-
end
|
|
203
|
-
when /\Ardoc-[a-z]+:/
|
|
204
|
-
CGI.escapeHTML($')
|
|
205
|
-
end
|
|
206
|
-
end
|
|
176
|
+
init_link_notation_regexp_handlings
|
|
177
|
+
end
|
|
207
178
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
end
|
|
179
|
+
##
|
|
180
|
+
# Adds regexp handlings about link notations.
|
|
211
181
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
182
|
+
def init_link_notation_regexp_handlings
|
|
183
|
+
add_regexp_handling_RDOCLINK
|
|
184
|
+
end
|
|
215
185
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
186
|
+
def handle_RDOCLINK(url) # :nodoc:
|
|
187
|
+
case url
|
|
188
|
+
when /^rdoc-ref:/
|
|
189
|
+
CGI.escapeHTML($')
|
|
190
|
+
when /^rdoc-label:/
|
|
191
|
+
return CGI.escapeHTML(url) if in_tidylink_label?
|
|
192
|
+
text = $'
|
|
193
|
+
|
|
194
|
+
text = case text
|
|
195
|
+
when /\Alabel-/ then $'
|
|
196
|
+
when /\Afootmark-/ then $'
|
|
197
|
+
when /\Afoottext-/ then $'
|
|
198
|
+
else text
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
|
|
202
|
+
when /^rdoc-image:/
|
|
203
|
+
# Split the string after "rdoc-image:" into url and alt.
|
|
204
|
+
# "path/to/image.jpg:alt text" => ["path/to/image.jpg", "alt text"]
|
|
205
|
+
# "http://example.com/path/to/image.jpg:alt text" => ["http://example.com/path/to/image.jpg", "alt text"]
|
|
206
|
+
url, alt = $'.split(/:(?!\/)/, 2)
|
|
207
|
+
if alt && !alt.empty?
|
|
208
|
+
%[<img src="#{CGI.escapeHTML(url)}" alt="#{CGI.escapeHTML(alt)}">]
|
|
209
|
+
else
|
|
210
|
+
%[<img src="#{CGI.escapeHTML(url)}">]
|
|
211
|
+
end
|
|
212
|
+
when /\Ardoc-[a-z]+:/
|
|
213
|
+
CGI.escapeHTML($')
|
|
214
|
+
end
|
|
215
|
+
end
|
|
221
216
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
emit_inline('</em>')
|
|
226
|
-
end
|
|
217
|
+
def handle_PLAIN_TEXT(text)
|
|
218
|
+
emit_inline(convert_string(text))
|
|
219
|
+
end
|
|
227
220
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
emit_inline('</strong>')
|
|
232
|
-
end
|
|
221
|
+
def handle_REGEXP_HANDLING_TEXT(text)
|
|
222
|
+
emit_inline(text)
|
|
223
|
+
end
|
|
233
224
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
225
|
+
def handle_BOLD(nodes)
|
|
226
|
+
emit_inline('<strong>')
|
|
227
|
+
super
|
|
228
|
+
emit_inline('</strong>')
|
|
229
|
+
end
|
|
239
230
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
231
|
+
def handle_EM(nodes)
|
|
232
|
+
emit_inline('<em>')
|
|
233
|
+
super
|
|
234
|
+
emit_inline('</em>')
|
|
235
|
+
end
|
|
245
236
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
237
|
+
def handle_BOLD_WORD(word)
|
|
238
|
+
emit_inline('<strong>')
|
|
239
|
+
super
|
|
240
|
+
emit_inline('</strong>')
|
|
241
|
+
end
|
|
251
242
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
243
|
+
def handle_EM_WORD(word)
|
|
244
|
+
emit_inline('<em>')
|
|
245
|
+
super
|
|
246
|
+
emit_inline('</em>')
|
|
247
|
+
end
|
|
255
248
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
249
|
+
def handle_TT(code)
|
|
250
|
+
emit_inline('<code>')
|
|
251
|
+
super
|
|
252
|
+
emit_inline('</code>')
|
|
253
|
+
end
|
|
259
254
|
|
|
260
|
-
|
|
255
|
+
def handle_STRIKE(nodes)
|
|
256
|
+
emit_inline('<del>')
|
|
257
|
+
super
|
|
258
|
+
emit_inline('</del>')
|
|
259
|
+
end
|
|
261
260
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
def handle_HARD_BREAK
|
|
262
|
+
emit_inline('<br>')
|
|
263
|
+
end
|
|
265
264
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
# but RDoc's test code expects the whole label part to be treated as RDOCLINK only in tidy link label.
|
|
270
|
-
# When a tidy link is <tt>{^1}[url]</tt> or <tt>{*1}[url]</tt>, the label part needs to drop leading * or ^.
|
|
271
|
-
# TODO: reconsider this workaround.
|
|
265
|
+
def emit_inline(text)
|
|
266
|
+
@inline_output << text
|
|
267
|
+
end
|
|
272
268
|
|
|
273
|
-
|
|
274
|
-
# ^1 *1 will be converted to just 1 in tidy link label.
|
|
275
|
-
return label[1..] if label.match?(/\A[*^]\d+\z/)
|
|
269
|
+
# Returns true if we are processing inside a tidy link label.
|
|
276
270
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
271
|
+
def in_tidylink_label?
|
|
272
|
+
@in_tidylink_label
|
|
273
|
+
end
|
|
280
274
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
emit_inline(handle_RDOCLINK(url))
|
|
288
|
-
return
|
|
289
|
-
end
|
|
275
|
+
# Special handling for tidy link labels.
|
|
276
|
+
# When a tidy link is <tt>{rdoc-image:path/to/image.jpg:alt text}[http://example.com]</tt>,
|
|
277
|
+
# label part is normally considered RDOCLINK <tt>rdoc-image:path/to/image.jpg:alt</tt> and a text <tt>" text"</tt>
|
|
278
|
+
# but RDoc's test code expects the whole label part to be treated as RDOCLINK only in tidy link label.
|
|
279
|
+
# When a tidy link is <tt>{^1}[url]</tt> or <tt>{*1}[url]</tt>, the label part needs to drop leading * or ^.
|
|
280
|
+
# TODO: reconsider this workaround.
|
|
290
281
|
|
|
291
|
-
|
|
292
|
-
|
|
282
|
+
def apply_tidylink_label_special_handling(label, url)
|
|
283
|
+
# ^1 *1 will be converted to just 1 in tidy link label.
|
|
284
|
+
return label[1..] if label.match?(/\A[*^]\d+\z/)
|
|
293
285
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
286
|
+
# rdoc-image in label specially allows spaces in alt text.
|
|
287
|
+
return handle_RDOCLINK(label) if label.start_with?('rdoc-image:')
|
|
288
|
+
end
|
|
297
289
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
emit_inline(
|
|
290
|
+
def handle_TIDYLINK(label_part, url)
|
|
291
|
+
# When url is an image, ignore label part (maybe bug?) and just generate img tag.
|
|
292
|
+
if url.match?(/\Ahttps?:\/\/.+\.(png|gif|jpg|jpeg|bmp)\z/)
|
|
293
|
+
emit_inline("<img src=\"#{CGI.escapeHTML(url)}\" />")
|
|
294
|
+
return
|
|
295
|
+
elsif url.match?(/\Ardoc-image:/)
|
|
296
|
+
emit_inline(handle_RDOCLINK(url))
|
|
302
297
|
return
|
|
303
298
|
end
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
299
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
valid_tag = open_tag && close_tag
|
|
310
|
-
emit_inline(open_tag) if valid_tag
|
|
311
|
-
@in_tidylink_label = true
|
|
312
|
-
traverse_inline_nodes(label_part)
|
|
313
|
-
@in_tidylink_label = false
|
|
314
|
-
emit_inline(close_tag) if valid_tag
|
|
315
|
-
end
|
|
300
|
+
if label_part.size == 1 && String === label_part[0]
|
|
301
|
+
raw_label = label_part[0]
|
|
316
302
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
super
|
|
321
|
-
out = @inline_output
|
|
322
|
-
@inline_output = nil
|
|
323
|
-
@quote_converter = nil
|
|
324
|
-
out
|
|
325
|
-
end
|
|
303
|
+
@in_tidylink_label = true
|
|
304
|
+
special = apply_tidylink_label_special_handling(raw_label, url)
|
|
305
|
+
@in_tidylink_label = false
|
|
326
306
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
307
|
+
if special
|
|
308
|
+
tag = gen_url(CGI.escapeHTML(url), special)
|
|
309
|
+
unless tag.empty?
|
|
310
|
+
emit_inline(tag)
|
|
311
|
+
return
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
332
315
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
316
|
+
tag = gen_url(CGI.escapeHTML(url), '')
|
|
317
|
+
open_tag, close_tag = tag.split(/(?=<\/a>)/, 2)
|
|
318
|
+
valid_tag = open_tag && close_tag
|
|
319
|
+
emit_inline(open_tag) if valid_tag
|
|
320
|
+
@in_tidylink_label = true
|
|
321
|
+
traverse_inline_nodes(label_part)
|
|
322
|
+
@in_tidylink_label = false
|
|
323
|
+
emit_inline(close_tag) if valid_tag
|
|
324
|
+
end
|
|
336
325
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
326
|
+
def handle_inline(text) # :nodoc:
|
|
327
|
+
@inline_output = +''
|
|
328
|
+
@quote_converter = QuoteConverter.new
|
|
329
|
+
super
|
|
330
|
+
out = @inline_output
|
|
331
|
+
@inline_output = nil
|
|
332
|
+
@quote_converter = nil
|
|
333
|
+
out
|
|
334
|
+
end
|
|
340
335
|
|
|
341
|
-
|
|
336
|
+
# Converts <tt>(c), (r), --, --- , ..., ...., ``, ''</tt> to HTML characters.
|
|
337
|
+
def handle_regexp_HTML_CHARACTERS(text)
|
|
338
|
+
name = HTML_CHARACTER_ALIASES[text]
|
|
339
|
+
TO_HTML_CHARACTERS[text.encoding][name] if name
|
|
340
|
+
end
|
|
342
341
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
def handle_regexp_QUOTE_NOT_AFTER_WORD(text)
|
|
343
|
+
@quote_converter.convert(text, after_word: false) || convert_string(text)
|
|
344
|
+
end
|
|
346
345
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
# <tt>mailto:</tt>::
|
|
351
|
-
# Inserted as-is.
|
|
352
|
-
# <tt>http:</tt>::
|
|
353
|
-
# Links are checked to see if they reference an image. If so, that image
|
|
354
|
-
# gets inserted using an <tt><img></tt> tag. Otherwise a conventional
|
|
355
|
-
# <tt><a href></tt> is used.
|
|
356
|
-
# <tt>link:</tt>::
|
|
357
|
-
# Reference to a local file relative to the output directory.
|
|
358
|
-
|
|
359
|
-
def handle_regexp_HYPERLINK(text)
|
|
360
|
-
return convert_string(text) if in_tidylink_label?
|
|
361
|
-
|
|
362
|
-
url = CGI.escapeHTML(text)
|
|
363
|
-
gen_url url, url
|
|
364
|
-
end
|
|
346
|
+
def handle_regexp_QUOTE_AFTER_WORD(text)
|
|
347
|
+
@quote_converter.convert(text, after_word: true) || convert_string(text)
|
|
348
|
+
end
|
|
365
349
|
|
|
366
|
-
|
|
367
|
-
# +target+ is an rdoc-schemed link that will be converted into a hyperlink.
|
|
368
|
-
#
|
|
369
|
-
# For the +rdoc-ref+ scheme the named reference will be returned without
|
|
370
|
-
# creating a link.
|
|
371
|
-
#
|
|
372
|
-
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
|
|
373
|
-
# when creating a link. All other contents will be linked verbatim.
|
|
374
|
-
|
|
375
|
-
def handle_regexp_RDOCLINK(text)
|
|
376
|
-
handle_RDOCLINK text
|
|
377
|
-
end
|
|
350
|
+
# Converts suppressed cross-reference +text+ to HTML by removing the leading backslash.
|
|
378
351
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
352
|
+
def handle_regexp_SUPPRESSED_CROSSREF(text)
|
|
353
|
+
convert_string(text.delete_prefix('\\'))
|
|
354
|
+
end
|
|
382
355
|
|
|
383
|
-
|
|
384
|
-
|
|
356
|
+
##
|
|
357
|
+
# +target+ is a potential link. The following schemes are handled:
|
|
358
|
+
#
|
|
359
|
+
# <tt>mailto:</tt>::
|
|
360
|
+
# Inserted as-is.
|
|
361
|
+
# <tt>http:</tt>::
|
|
362
|
+
# Links are checked to see if they reference an image. If so, that image
|
|
363
|
+
# gets inserted using an <tt><img></tt> tag. Otherwise a conventional
|
|
364
|
+
# <tt><a href></tt> is used.
|
|
365
|
+
# <tt>link:</tt>::
|
|
366
|
+
# Reference to a local file relative to the output directory.
|
|
367
|
+
|
|
368
|
+
def handle_regexp_HYPERLINK(text)
|
|
369
|
+
return convert_string(text) if in_tidylink_label?
|
|
370
|
+
|
|
371
|
+
url = CGI.escapeHTML(text)
|
|
372
|
+
gen_url url, url
|
|
373
|
+
end
|
|
385
374
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
375
|
+
##
|
|
376
|
+
# +target+ is an rdoc-schemed link that will be converted into a hyperlink.
|
|
377
|
+
#
|
|
378
|
+
# For the +rdoc-ref+ scheme the named reference will be returned without
|
|
379
|
+
# creating a link.
|
|
380
|
+
#
|
|
381
|
+
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
|
|
382
|
+
# when creating a link. All other contents will be linked verbatim.
|
|
383
|
+
|
|
384
|
+
def handle_regexp_RDOCLINK(text)
|
|
385
|
+
handle_RDOCLINK text
|
|
386
|
+
end
|
|
392
387
|
|
|
393
|
-
|
|
394
|
-
|
|
388
|
+
# :section: Visitor
|
|
389
|
+
#
|
|
390
|
+
# These methods implement the HTML visitor.
|
|
395
391
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
end
|
|
392
|
+
##
|
|
393
|
+
# Prepares the visitor for HTML generation
|
|
399
394
|
|
|
400
|
-
|
|
401
|
-
|
|
395
|
+
def start_accepting
|
|
396
|
+
@res = []
|
|
397
|
+
@in_list_entry = []
|
|
398
|
+
@list = []
|
|
399
|
+
@heading_ids = {}
|
|
400
|
+
end
|
|
402
401
|
|
|
403
|
-
|
|
404
|
-
|
|
402
|
+
##
|
|
403
|
+
# Returns the generated output
|
|
405
404
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
405
|
+
def end_accepting
|
|
406
|
+
@res.join
|
|
407
|
+
end
|
|
409
408
|
|
|
410
|
-
|
|
411
|
-
|
|
409
|
+
##
|
|
410
|
+
# Adds +block_quote+ to the output
|
|
412
411
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def accept_paragraph(paragraph)
|
|
417
|
-
@res << "\n<p>"
|
|
418
|
-
text = paragraph.text @hard_break
|
|
419
|
-
text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K(?:\r?\n)+(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
|
|
420
|
-
defined?($2) && ' '
|
|
421
|
-
}
|
|
422
|
-
@res << to_html(text)
|
|
423
|
-
@res << "</p>\n"
|
|
424
|
-
end
|
|
412
|
+
def accept_block_quote(block_quote)
|
|
413
|
+
@res << "\n<blockquote>"
|
|
425
414
|
|
|
426
|
-
|
|
415
|
+
block_quote.parts.each do |part|
|
|
416
|
+
part.accept self
|
|
417
|
+
end
|
|
427
418
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
result = RDoc::TokenStream.to_html tokens
|
|
431
|
-
result = result + "\n" unless result.end_with?("\n")
|
|
432
|
-
result
|
|
433
|
-
end
|
|
419
|
+
@res << "</blockquote>\n"
|
|
420
|
+
end
|
|
434
421
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
content = parsable_text_to_html(text)
|
|
448
|
-
klass = ' class="ruby"'
|
|
449
|
-
else
|
|
450
|
-
content = CGI.escapeHTML text
|
|
451
|
-
klass = " class=\"#{format}\"" if format
|
|
452
|
-
end
|
|
422
|
+
##
|
|
423
|
+
# Adds +paragraph+ to the output
|
|
424
|
+
|
|
425
|
+
def accept_paragraph(paragraph)
|
|
426
|
+
@res << "\n<p>"
|
|
427
|
+
text = paragraph.text @hard_break
|
|
428
|
+
text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K(?:\r?\n)+(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
|
|
429
|
+
defined?($2) && ' '
|
|
430
|
+
}
|
|
431
|
+
@res << to_html(text)
|
|
432
|
+
@res << "</p>\n"
|
|
433
|
+
end
|
|
453
434
|
|
|
454
|
-
|
|
455
|
-
@res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
|
|
456
|
-
else
|
|
457
|
-
@res << "\n<pre#{klass}>#{content}</pre>\n"
|
|
458
|
-
end
|
|
459
|
-
end
|
|
435
|
+
# Generate syntax highlighted html for ruby-like text.
|
|
460
436
|
|
|
461
|
-
|
|
462
|
-
|
|
437
|
+
def parsable_text_to_html(text)
|
|
438
|
+
tokens = ::RDoc::Parser::RubyColorizer.colorize(text)
|
|
439
|
+
result = TokenStream.to_html tokens
|
|
440
|
+
result = result + "\n" unless result.end_with?("\n")
|
|
441
|
+
result
|
|
442
|
+
end
|
|
463
443
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
end
|
|
444
|
+
##
|
|
445
|
+
# Adds +verbatim+ to the output
|
|
467
446
|
|
|
468
|
-
|
|
469
|
-
|
|
447
|
+
def accept_verbatim(verbatim)
|
|
448
|
+
text = verbatim.text.rstrip
|
|
449
|
+
format = verbatim.format
|
|
470
450
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
451
|
+
# Apply Ruby syntax highlighting if
|
|
452
|
+
# - explicitly marked as Ruby (via ruby? which accepts :ruby or :rb)
|
|
453
|
+
# - no format specified but the text is parseable as Ruby
|
|
454
|
+
# Otherwise, add language class when applicable and skip Ruby highlighting
|
|
455
|
+
if verbatim.ruby? || (format.nil? && parseable?(text))
|
|
456
|
+
content = parsable_text_to_html(text)
|
|
457
|
+
klass = ' class="ruby"'
|
|
458
|
+
else
|
|
459
|
+
content = CGI.escapeHTML text
|
|
460
|
+
klass = " class=\"#{format}\"" if format
|
|
461
|
+
end
|
|
476
462
|
|
|
477
|
-
|
|
478
|
-
|
|
463
|
+
if @pipe
|
|
464
|
+
@res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
|
|
465
|
+
else
|
|
466
|
+
@res << "\n<pre#{klass}>#{content}</pre>\n"
|
|
467
|
+
end
|
|
468
|
+
end
|
|
479
469
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if tag = @in_list_entry.pop
|
|
483
|
-
@res << tag
|
|
484
|
-
end
|
|
485
|
-
@res << html_list_name(list.type, false) << "\n"
|
|
486
|
-
end
|
|
470
|
+
##
|
|
471
|
+
# Adds +rule+ to the output
|
|
487
472
|
|
|
488
|
-
|
|
489
|
-
|
|
473
|
+
def accept_rule(rule)
|
|
474
|
+
@res << "<hr>\n"
|
|
475
|
+
end
|
|
490
476
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
@res << tag
|
|
494
|
-
end
|
|
477
|
+
##
|
|
478
|
+
# Prepares the visitor for consuming +list+
|
|
495
479
|
|
|
496
|
-
|
|
497
|
-
|
|
480
|
+
def accept_list_start(list)
|
|
481
|
+
@list << list.type
|
|
482
|
+
@res << html_list_name(list.type, true)
|
|
483
|
+
@in_list_entry.push false
|
|
484
|
+
end
|
|
498
485
|
|
|
499
|
-
|
|
500
|
-
|
|
486
|
+
##
|
|
487
|
+
# Finishes consumption of +list+
|
|
501
488
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
489
|
+
def accept_list_end(list)
|
|
490
|
+
@list.pop
|
|
491
|
+
if tag = @in_list_entry.pop
|
|
492
|
+
@res << tag
|
|
493
|
+
end
|
|
494
|
+
@res << html_list_name(list.type, false) << "\n"
|
|
495
|
+
end
|
|
505
496
|
|
|
506
|
-
|
|
507
|
-
|
|
497
|
+
##
|
|
498
|
+
# Prepares the visitor for consuming +list_item+
|
|
508
499
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
500
|
+
def accept_list_item_start(list_item)
|
|
501
|
+
if tag = @in_list_entry.last
|
|
502
|
+
@res << tag
|
|
503
|
+
end
|
|
512
504
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
# level 6.
|
|
505
|
+
@res << list_item_start(list_item, @list.last)
|
|
506
|
+
end
|
|
516
507
|
|
|
517
|
-
|
|
518
|
-
|
|
508
|
+
##
|
|
509
|
+
# Finishes consumption of +list_item+
|
|
519
510
|
|
|
520
|
-
|
|
521
|
-
|
|
511
|
+
def accept_list_item_end(list_item)
|
|
512
|
+
@in_list_entry[-1] = list_end_for(@list.last)
|
|
513
|
+
end
|
|
522
514
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
if @output_decoration && !@pipe
|
|
526
|
-
@res << "\n<span id=\"#{legacy_label}\" class=\"legacy-anchor\"></span>"
|
|
527
|
-
end
|
|
515
|
+
##
|
|
516
|
+
# Adds +blank_line+ to the output
|
|
528
517
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
"\n<h#{level}>"
|
|
533
|
-
end
|
|
518
|
+
def accept_blank_line(blank_line)
|
|
519
|
+
# @res << annotate("<p />") << "\n"
|
|
520
|
+
end
|
|
534
521
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
@res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
|
|
539
|
-
end
|
|
522
|
+
##
|
|
523
|
+
# Adds +heading+ to the output. The headings greater than 6 are trimmed to
|
|
524
|
+
# level 6.
|
|
540
525
|
|
|
541
|
-
|
|
542
|
-
|
|
526
|
+
def accept_heading(heading)
|
|
527
|
+
level = [6, heading.level].min
|
|
543
528
|
|
|
544
|
-
|
|
545
|
-
|
|
529
|
+
label = deduplicate_heading_id(heading.label(@code_object))
|
|
530
|
+
legacy_label = deduplicate_heading_id(heading.legacy_label(@code_object))
|
|
546
531
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
532
|
+
# Add legacy anchor before the heading for backward compatibility.
|
|
533
|
+
# This allows old links with label- prefix to still work.
|
|
534
|
+
if @output_decoration && !@pipe
|
|
535
|
+
@res << "\n<span id=\"#{legacy_label}\" class=\"legacy-anchor\"></span>"
|
|
536
|
+
end
|
|
550
537
|
|
|
551
|
-
|
|
552
|
-
|
|
538
|
+
@res << if @output_decoration
|
|
539
|
+
"\n<h#{level} id=\"#{label}\">"
|
|
540
|
+
else
|
|
541
|
+
"\n<h#{level}>"
|
|
542
|
+
end
|
|
553
543
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
@res << '>' << to_html(text) << "</th>\n"
|
|
560
|
-
end
|
|
561
|
-
@res << "</tr>\n</thead>\n<tbody>\n"
|
|
562
|
-
body.each do |row|
|
|
563
|
-
@res << "<tr>\n"
|
|
564
|
-
row.zip(aligns) do |text, align|
|
|
565
|
-
@res << '<td'
|
|
566
|
-
@res << ' align="' << align << '"' if align
|
|
567
|
-
@res << '>' << to_html(text) << "</td>\n"
|
|
568
|
-
end
|
|
569
|
-
@res << "</tr>\n"
|
|
570
|
-
end
|
|
571
|
-
@res << "</tbody>\n</table>\n"
|
|
572
|
-
end
|
|
544
|
+
if @pipe
|
|
545
|
+
@res << to_html(heading.text)
|
|
546
|
+
else
|
|
547
|
+
@res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
|
|
548
|
+
end
|
|
573
549
|
|
|
574
|
-
|
|
550
|
+
@res << "</h#{level}>\n"
|
|
551
|
+
end
|
|
575
552
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
# Matches GitHub's behavior for duplicate heading anchors.
|
|
553
|
+
##
|
|
554
|
+
# Adds +raw+ to the output
|
|
579
555
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
"#{id}-#{@heading_ids[id]}"
|
|
584
|
-
else
|
|
585
|
-
@heading_ids[id] = 0
|
|
586
|
-
id
|
|
587
|
-
end
|
|
588
|
-
end
|
|
556
|
+
def accept_raw(raw)
|
|
557
|
+
@res << raw.parts.join("\n")
|
|
558
|
+
end
|
|
589
559
|
|
|
590
|
-
|
|
591
|
-
|
|
560
|
+
##
|
|
561
|
+
# Adds +table+ to the output
|
|
592
562
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
563
|
+
def accept_table(header, body, aligns)
|
|
564
|
+
@res << "\n<table role=\"table\">\n<thead>\n<tr>\n"
|
|
565
|
+
header.zip(aligns) do |text, align|
|
|
566
|
+
@res << '<th'
|
|
567
|
+
@res << ' align="' << align << '"' if align
|
|
568
|
+
@res << '>' << to_html(text) << "</th>\n"
|
|
569
|
+
end
|
|
570
|
+
@res << "</tr>\n</thead>\n<tbody>\n"
|
|
571
|
+
body.each do |row|
|
|
572
|
+
@res << "<tr>\n"
|
|
573
|
+
row.zip(aligns) do |text, align|
|
|
574
|
+
@res << '<td'
|
|
575
|
+
@res << ' align="' << align << '"' if align
|
|
576
|
+
@res << '>' << to_html(text) << "</td>\n"
|
|
577
|
+
end
|
|
578
|
+
@res << "</tr>\n"
|
|
579
|
+
end
|
|
580
|
+
@res << "</tbody>\n</table>\n"
|
|
581
|
+
end
|
|
596
582
|
|
|
597
|
-
|
|
598
|
-
# Generates an HTML link or image tag for the given +url+ and +text+.
|
|
599
|
-
#
|
|
600
|
-
# - Image URLs (http/https/link ending in .gif, .png, .jpg, .jpeg, .bmp)
|
|
601
|
-
# become <img> tags
|
|
602
|
-
# - File references (.rb, .rdoc, .md) are converted to .html paths
|
|
603
|
-
# - Anchor URLs (#foo) pass through unchanged for GitHub-style header linking
|
|
604
|
-
# - Footnote links get wrapped in <sup> tags
|
|
583
|
+
# :section: Utilities
|
|
605
584
|
|
|
606
|
-
|
|
607
|
-
|
|
585
|
+
##
|
|
586
|
+
# Returns a unique heading ID, appending -1, -2, etc. for duplicates.
|
|
587
|
+
# Matches GitHub's behavior for duplicate heading anchors.
|
|
608
588
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
589
|
+
def deduplicate_heading_id(id)
|
|
590
|
+
if @heading_ids.key?(id)
|
|
591
|
+
@heading_ids[id] += 1
|
|
592
|
+
"#{id}-#{@heading_ids[id]}"
|
|
593
|
+
else
|
|
594
|
+
@heading_ids[id] = 0
|
|
595
|
+
id
|
|
596
|
+
end
|
|
614
597
|
end
|
|
615
598
|
|
|
616
|
-
|
|
617
|
-
|
|
599
|
+
##
|
|
600
|
+
# CGI-escapes +text+
|
|
601
|
+
|
|
602
|
+
def convert_string(text)
|
|
603
|
+
CGI.escapeHTML text
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
##
|
|
607
|
+
# Generates an HTML link or image tag for the given +url+ and +text+.
|
|
608
|
+
#
|
|
609
|
+
# - Image URLs (http/https/link ending in .gif, .png, .jpg, .jpeg, .bmp)
|
|
610
|
+
# become <img> tags
|
|
611
|
+
# - File references (.rb, .rdoc, .md) are converted to .html paths
|
|
612
|
+
# - Anchor URLs (#foo) pass through unchanged for GitHub-style header linking
|
|
613
|
+
# - Footnote links get wrapped in <sup> tags
|
|
614
|
+
# - URLs with an unsafe scheme (javascript:, vbscript:, data:, file:) are not
|
|
615
|
+
# linked and only +text+ is returned
|
|
616
|
+
|
|
617
|
+
def gen_url(url, text)
|
|
618
|
+
scheme, url, id = parse_url url
|
|
619
|
+
|
|
620
|
+
# Check the parsed URL because link: URLs are used as href without it
|
|
621
|
+
return text if unsafe_url?(url)
|
|
622
|
+
|
|
623
|
+
if %w[http https link].include?(scheme) && url =~ /\.(gif|png|jpg|jpeg|bmp)\z/
|
|
624
|
+
"<img src=\"#{url}\" />"
|
|
625
|
+
else
|
|
626
|
+
if scheme != 'link' and %r%\A((?!https?:)(?:[^/#]*/)*+)([^/#]+)\.(rb|rdoc|md)(?=\z|#)%i =~ url
|
|
627
|
+
url = "#$1#{$2.tr('.', '_')}_#$3.html#$'"
|
|
628
|
+
end
|
|
618
629
|
|
|
619
|
-
|
|
630
|
+
text = text.sub %r%^#{scheme}:/*%i, ''
|
|
631
|
+
text = text.sub %r%^[*\^](\d+)$%, '\1'
|
|
620
632
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
633
|
+
link = "<a#{id} href=\"#{url}\">#{text}</a>"
|
|
634
|
+
|
|
635
|
+
if /"foot/.match?(id)
|
|
636
|
+
"<sup>#{link}</sup>"
|
|
637
|
+
else
|
|
638
|
+
link
|
|
639
|
+
end
|
|
640
|
+
end
|
|
625
641
|
end
|
|
626
|
-
end
|
|
627
|
-
end
|
|
628
642
|
|
|
629
|
-
|
|
630
|
-
|
|
643
|
+
##
|
|
644
|
+
# Does +url+ have a scheme in UNSAFE_URL_SCHEME_REGEXP?
|
|
645
|
+
#
|
|
646
|
+
# Browsers ignore leading C0 controls and spaces and all ASCII tabs and
|
|
647
|
+
# newlines in a URL, so they are removed before checking the scheme.
|
|
648
|
+
# See https://url.spec.whatwg.org/#concept-basic-url-parser
|
|
649
|
+
#
|
|
650
|
+
#: (String) -> bool
|
|
651
|
+
def unsafe_url?(url)
|
|
652
|
+
normalized = url.sub(/\A[\x00-\x20]+/, '').delete("\t\n\r")
|
|
653
|
+
UNSAFE_URL_SCHEME_REGEXP.match?(normalized)
|
|
654
|
+
end
|
|
631
655
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
|
|
635
|
-
tags[open_tag ? 0 : 1]
|
|
636
|
-
end
|
|
656
|
+
##
|
|
657
|
+
# Determines the HTML list element for +list_type+ and +open_tag+
|
|
637
658
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
case list_type
|
|
644
|
-
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
|
|
645
|
-
"<li>"
|
|
646
|
-
when :LABEL, :NOTE then
|
|
647
|
-
Array(list_item.label).map do |label|
|
|
648
|
-
"<dt>#{to_html label}</dt>\n"
|
|
649
|
-
end.join << "<dd>"
|
|
650
|
-
else
|
|
651
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
|
|
652
|
-
end
|
|
653
|
-
end
|
|
659
|
+
def html_list_name(list_type, open_tag)
|
|
660
|
+
tags = LIST_TYPE_TO_HTML[list_type]
|
|
661
|
+
raise Error, "Invalid list type: #{list_type.inspect}" unless tags
|
|
662
|
+
tags[open_tag ? 0 : 1]
|
|
663
|
+
end
|
|
654
664
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
665
|
+
##
|
|
666
|
+
# Returns the HTML tag for +list_type+, possible using a label from
|
|
667
|
+
# +list_item+
|
|
668
|
+
|
|
669
|
+
def list_item_start(list_item, list_type)
|
|
670
|
+
case list_type
|
|
671
|
+
when :BULLET, :LALPHA, :NUMBER, :UALPHA
|
|
672
|
+
"<li>"
|
|
673
|
+
when :LABEL, :NOTE
|
|
674
|
+
Array(list_item.label).map do |label|
|
|
675
|
+
"<dt>#{to_html label}</dt>\n"
|
|
676
|
+
end.join << "<dd>"
|
|
677
|
+
else
|
|
678
|
+
raise Error, "Invalid list type: #{list_type.inspect}"
|
|
679
|
+
end
|
|
680
|
+
end
|
|
668
681
|
|
|
669
|
-
|
|
670
|
-
|
|
682
|
+
##
|
|
683
|
+
# Returns the HTML end-tag for +list_type+
|
|
671
684
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
685
|
+
def list_end_for(list_type)
|
|
686
|
+
case list_type
|
|
687
|
+
when :BULLET, :LALPHA, :NUMBER, :UALPHA
|
|
688
|
+
"</li>"
|
|
689
|
+
when :LABEL, :NOTE
|
|
690
|
+
"</dd>"
|
|
691
|
+
else
|
|
692
|
+
raise Error, "Invalid list type: #{list_type.inspect}"
|
|
693
|
+
end
|
|
694
|
+
end
|
|
675
695
|
|
|
676
|
-
|
|
677
|
-
|
|
696
|
+
##
|
|
697
|
+
# Returns true if text is valid ruby syntax
|
|
678
698
|
|
|
679
|
-
|
|
680
|
-
|
|
699
|
+
def parseable?(text)
|
|
700
|
+
Prism.parse_success?(text)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
##
|
|
704
|
+
# Converts +item+ to HTML using RDoc::Text#to_html
|
|
705
|
+
|
|
706
|
+
def to_html(item)
|
|
707
|
+
handle_inline(item)
|
|
708
|
+
end
|
|
709
|
+
end
|
|
681
710
|
end
|
|
682
711
|
end
|