rdoc 7.2.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 +4 -7
- data/LICENSE.rdoc +4 -0
- data/README.md +43 -2
- data/RI.md +75 -75
- data/doc/markup_reference/markdown.md +104 -3
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +70 -74
- data/lib/rdoc/code_object/any_method.rb +305 -298
- data/lib/rdoc/code_object/attr.rb +150 -143
- data/lib/rdoc/code_object/class_module.rb +801 -765
- data/lib/rdoc/code_object/constant.rb +178 -150
- data/lib/rdoc/code_object/context/section.rb +133 -160
- data/lib/rdoc/code_object/context.rb +925 -952
- 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 +325 -324
- 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 -213
- data/lib/rdoc/code_object.rb +305 -305
- data/lib/rdoc/comment.rb +274 -337
- data/lib/rdoc/cross_reference.rb +194 -212
- 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 +165 -140
- data/lib/rdoc/generator/darkfish.rb +647 -631
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +165 -122
- 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 +538 -0
- data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +11 -11
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
- data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
- data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +2 -2
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/class.rhtml +56 -46
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +538 -283
- data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/js/aliki.js +80 -102
- data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
- data/lib/rdoc/generator/template/darkfish/class.rhtml +18 -21
- data/lib/rdoc/generator/template/darkfish/css/rdoc.css +0 -1
- data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
- 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 +30 -21
- data/lib/rdoc/markdown.rb +329 -151
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +215 -221
- data/lib/rdoc/markup/heading.rb +1 -4
- 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 +284 -305
- 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 +600 -493
- data/lib/rdoc/markup/to_html_crossref.rb +221 -191
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
- 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 +83 -86
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1076
- data/lib/rdoc/parser/c.rb +1039 -1036
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +279 -0
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1231 -2222
- data/lib/rdoc/parser/ruby_colorizer.rb +303 -0
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +230 -221
- data/lib/rdoc/rbs_helper.rb +186 -0
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +547 -366
- data/lib/rdoc/ri/driver.rb +1141 -1130
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +456 -0
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -11
- data/lib/rdoc/rubygems_hook.rb +194 -192
- data/lib/rdoc/server.rb +462 -0
- 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 +363 -338
- data/lib/rdoc/store.rb +919 -725
- data/lib/rdoc/task.rb +260 -255
- data/lib/rdoc/text.rb +130 -245
- data/lib/rdoc/token_stream.rb +101 -115
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- data/lib/rdoc.rb +35 -7
- data/lib/rubygems_plugin.rb +2 -11
- data/rdoc-logo.svg +43 -0
- data/rdoc.gemspec +6 -4
- metadata +36 -20
- data/lib/rdoc/code_object/anon_class.rb +0 -10
- data/lib/rdoc/code_object/ghost_method.rb +0 -6
- data/lib/rdoc/code_object/meta_method.rb +0 -6
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
- data/lib/rdoc/parser/prism_ruby.rb +0 -1112
- data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
- data/lib/rdoc/parser/ruby_tools.rb +0 -163
- data/lib/rdoc/servlet.rb +0 -452
data/lib/rdoc/markup/to_html.rb
CHANGED
|
@@ -1,604 +1,711 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'cgi/escape'
|
|
3
3
|
require 'cgi/util' unless defined?(CGI::EscapeExt)
|
|
4
|
+
require 'prism'
|
|
5
|
+
|
|
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
|
|
4
65
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
7
90
|
|
|
8
|
-
|
|
91
|
+
# Converts ascii quote pairs to multibyte quote characters
|
|
92
|
+
class QuoteConverter
|
|
9
93
|
|
|
10
|
-
|
|
94
|
+
def initialize
|
|
95
|
+
@in_dquote = false
|
|
96
|
+
@in_squote = false
|
|
97
|
+
end
|
|
11
98
|
|
|
12
|
-
|
|
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
|
|
124
|
+
end
|
|
125
|
+
end
|
|
13
126
|
|
|
14
|
-
|
|
15
|
-
|
|
127
|
+
##
|
|
128
|
+
# Creates a new formatter that will output HTML
|
|
16
129
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
:LABEL => ['<dl class="rdoc-list label-list">', '</dl>'],
|
|
20
|
-
:LALPHA => ['<ol style="list-style-type: lower-alpha">', '</ol>'],
|
|
21
|
-
:NOTE => ['<dl class="rdoc-list note-list">', '</dl>'],
|
|
22
|
-
:NUMBER => ['<ol>', '</ol>'],
|
|
23
|
-
:UALPHA => ['<ol style="list-style-type: upper-alpha">', '</ol>'],
|
|
24
|
-
}
|
|
130
|
+
def initialize(pipe: false, output_decoration: true)
|
|
131
|
+
super()
|
|
25
132
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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"
|
|
29
143
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# generate namespaced URI fragments
|
|
144
|
+
init_regexp_handlings
|
|
145
|
+
end
|
|
33
146
|
|
|
34
|
-
|
|
147
|
+
# :section: Regexp Handling
|
|
148
|
+
#
|
|
149
|
+
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
35
150
|
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
|
38
154
|
|
|
39
|
-
|
|
155
|
+
UNSAFE_URL_SCHEME_REGEXP = /\A(?:javascript|vbscript|data|file):/i
|
|
40
156
|
|
|
41
|
-
|
|
157
|
+
# :nodoc:
|
|
158
|
+
URL_CHARACTERS_REGEXP_STR = /[A-Za-z0-9\-._~:\/\?#\[\]@!$&'\(\)*+,;%=]/.source
|
|
42
159
|
|
|
43
|
-
|
|
44
|
-
|
|
160
|
+
##
|
|
161
|
+
# Adds regexp handlings.
|
|
45
162
|
|
|
46
|
-
|
|
47
|
-
|
|
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)
|
|
48
167
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@in_list_entry = nil
|
|
52
|
-
@list = nil
|
|
53
|
-
@th = nil
|
|
54
|
-
@in_tidylink_label = false
|
|
55
|
-
@hard_break = "<br>\n"
|
|
168
|
+
# suppress crossref: \#method \::method \ClassName \method_with_underscores
|
|
169
|
+
@markup.add_regexp_handling(/\\(?:[#:A-Z]|[a-z]+_[a-z0-9])/, :SUPPRESSED_CROSSREF)
|
|
56
170
|
|
|
57
|
-
|
|
58
|
-
end
|
|
171
|
+
@markup.add_regexp_handling(HTML_CHARACTERS_REGEXP, :HTML_CHARACTERS)
|
|
59
172
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
173
|
+
@markup.add_regexp_handling(/\b['"`]/, :QUOTE_AFTER_WORD)
|
|
174
|
+
@markup.add_regexp_handling(/\B['"`]/, :QUOTE_NOT_AFTER_WORD)
|
|
63
175
|
|
|
64
|
-
|
|
65
|
-
|
|
176
|
+
init_link_notation_regexp_handlings
|
|
177
|
+
end
|
|
66
178
|
|
|
67
|
-
|
|
68
|
-
|
|
179
|
+
##
|
|
180
|
+
# Adds regexp handlings about link notations.
|
|
69
181
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
:HYPERLINK)
|
|
182
|
+
def init_link_notation_regexp_handlings
|
|
183
|
+
add_regexp_handling_RDOCLINK
|
|
184
|
+
end
|
|
74
185
|
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
216
|
|
|
78
|
-
|
|
79
|
-
|
|
217
|
+
def handle_PLAIN_TEXT(text)
|
|
218
|
+
emit_inline(convert_string(text))
|
|
219
|
+
end
|
|
80
220
|
|
|
81
|
-
|
|
82
|
-
|
|
221
|
+
def handle_REGEXP_HANDLING_TEXT(text)
|
|
222
|
+
emit_inline(text)
|
|
223
|
+
end
|
|
83
224
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
225
|
+
def handle_BOLD(nodes)
|
|
226
|
+
emit_inline('<strong>')
|
|
227
|
+
super
|
|
228
|
+
emit_inline('</strong>')
|
|
229
|
+
end
|
|
87
230
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return CGI.escapeHTML(url) if in_tidylink_label?
|
|
94
|
-
text = $'
|
|
95
|
-
|
|
96
|
-
text = case text
|
|
97
|
-
when /\Alabel-/ then $'
|
|
98
|
-
when /\Afootmark-/ then $'
|
|
99
|
-
when /\Afoottext-/ then $'
|
|
100
|
-
else text
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
|
|
104
|
-
when /^rdoc-image:/
|
|
105
|
-
# Split the string after "rdoc-image:" into url and alt.
|
|
106
|
-
# "path/to/image.jpg:alt text" => ["path/to/image.jpg", "alt text"]
|
|
107
|
-
# "http://example.com/path/to/image.jpg:alt text" => ["http://example.com/path/to/image.jpg", "alt text"]
|
|
108
|
-
url, alt = $'.split(/:(?!\/)/, 2)
|
|
109
|
-
if alt && !alt.empty?
|
|
110
|
-
%[<img src="#{CGI.escapeHTML(url)}" alt="#{CGI.escapeHTML(alt)}">]
|
|
111
|
-
else
|
|
112
|
-
%[<img src="#{CGI.escapeHTML(url)}">]
|
|
113
|
-
end
|
|
114
|
-
when /\Ardoc-[a-z]+:/
|
|
115
|
-
CGI.escapeHTML($')
|
|
116
|
-
end
|
|
117
|
-
end
|
|
231
|
+
def handle_EM(nodes)
|
|
232
|
+
emit_inline('<em>')
|
|
233
|
+
super
|
|
234
|
+
emit_inline('</em>')
|
|
235
|
+
end
|
|
118
236
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
237
|
+
def handle_BOLD_WORD(word)
|
|
238
|
+
emit_inline('<strong>')
|
|
239
|
+
super
|
|
240
|
+
emit_inline('</strong>')
|
|
241
|
+
end
|
|
122
242
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
243
|
+
def handle_EM_WORD(word)
|
|
244
|
+
emit_inline('<em>')
|
|
245
|
+
super
|
|
246
|
+
emit_inline('</em>')
|
|
247
|
+
end
|
|
126
248
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
249
|
+
def handle_TT(code)
|
|
250
|
+
emit_inline('<code>')
|
|
251
|
+
super
|
|
252
|
+
emit_inline('</code>')
|
|
253
|
+
end
|
|
132
254
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
255
|
+
def handle_STRIKE(nodes)
|
|
256
|
+
emit_inline('<del>')
|
|
257
|
+
super
|
|
258
|
+
emit_inline('</del>')
|
|
259
|
+
end
|
|
138
260
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
emit_inline('</strong>')
|
|
143
|
-
end
|
|
261
|
+
def handle_HARD_BREAK
|
|
262
|
+
emit_inline('<br>')
|
|
263
|
+
end
|
|
144
264
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
emit_inline('</em>')
|
|
149
|
-
end
|
|
265
|
+
def emit_inline(text)
|
|
266
|
+
@inline_output << text
|
|
267
|
+
end
|
|
150
268
|
|
|
151
|
-
|
|
152
|
-
emit_inline('<code>')
|
|
153
|
-
super
|
|
154
|
-
emit_inline('</code>')
|
|
155
|
-
end
|
|
269
|
+
# Returns true if we are processing inside a tidy link label.
|
|
156
270
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
emit_inline('</del>')
|
|
161
|
-
end
|
|
271
|
+
def in_tidylink_label?
|
|
272
|
+
@in_tidylink_label
|
|
273
|
+
end
|
|
162
274
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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.
|
|
166
281
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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/)
|
|
170
285
|
|
|
171
|
-
|
|
286
|
+
# rdoc-image in label specially allows spaces in alt text.
|
|
287
|
+
return handle_RDOCLINK(label) if label.start_with?('rdoc-image:')
|
|
288
|
+
end
|
|
172
289
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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))
|
|
297
|
+
return
|
|
298
|
+
end
|
|
176
299
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
# label part is normally considered RDOCLINK <tt>rdoc-image:path/to/image.jpg:alt</tt> and a text <tt>" text"</tt>
|
|
180
|
-
# but RDoc's test code expects the whole label part to be treated as RDOCLINK only in tidy link label.
|
|
181
|
-
# When a tidy link is <tt>{^1}[url]</tt> or <tt>{*1}[url]</tt>, the label part needs to drop leading * or ^.
|
|
182
|
-
# TODO: reconsider this workaround.
|
|
300
|
+
if label_part.size == 1 && String === label_part[0]
|
|
301
|
+
raw_label = label_part[0]
|
|
183
302
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
303
|
+
@in_tidylink_label = true
|
|
304
|
+
special = apply_tidylink_label_special_handling(raw_label, url)
|
|
305
|
+
@in_tidylink_label = false
|
|
187
306
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
191
315
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|
201
325
|
|
|
202
|
-
|
|
203
|
-
|
|
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
|
|
204
335
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
208
341
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
unless tag.empty?
|
|
212
|
-
emit_inline(tag)
|
|
213
|
-
return
|
|
214
|
-
end
|
|
342
|
+
def handle_regexp_QUOTE_NOT_AFTER_WORD(text)
|
|
343
|
+
@quote_converter.convert(text, after_word: false) || convert_string(text)
|
|
215
344
|
end
|
|
216
|
-
end
|
|
217
345
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
emit_inline(open_tag) if valid_tag
|
|
222
|
-
@in_tidylink_label = true
|
|
223
|
-
traverse_inline_nodes(label_part)
|
|
224
|
-
@in_tidylink_label = false
|
|
225
|
-
emit_inline(close_tag) if valid_tag
|
|
226
|
-
end
|
|
346
|
+
def handle_regexp_QUOTE_AFTER_WORD(text)
|
|
347
|
+
@quote_converter.convert(text, after_word: true) || convert_string(text)
|
|
348
|
+
end
|
|
227
349
|
|
|
228
|
-
|
|
229
|
-
@inline_output = +''
|
|
230
|
-
super
|
|
231
|
-
out = @inline_output
|
|
232
|
-
@inline_output = nil
|
|
233
|
-
out
|
|
234
|
-
end
|
|
350
|
+
# Converts suppressed cross-reference +text+ to HTML by removing the leading backslash.
|
|
235
351
|
|
|
236
|
-
|
|
352
|
+
def handle_regexp_SUPPRESSED_CROSSREF(text)
|
|
353
|
+
convert_string(text.delete_prefix('\\'))
|
|
354
|
+
end
|
|
237
355
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
|
241
374
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def handle_regexp_HYPERLINK(text)
|
|
255
|
-
return convert_string(text) if in_tidylink_label?
|
|
256
|
-
|
|
257
|
-
url = CGI.escapeHTML(text)
|
|
258
|
-
gen_url url, url
|
|
259
|
-
end
|
|
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
|
|
260
387
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
# For the +rdoc-ref+ scheme the named reference will be returned without
|
|
265
|
-
# creating a link.
|
|
266
|
-
#
|
|
267
|
-
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
|
|
268
|
-
# when creating a link. All other contents will be linked verbatim.
|
|
269
|
-
|
|
270
|
-
def handle_regexp_RDOCLINK(text)
|
|
271
|
-
handle_RDOCLINK text
|
|
272
|
-
end
|
|
388
|
+
# :section: Visitor
|
|
389
|
+
#
|
|
390
|
+
# These methods implement the HTML visitor.
|
|
273
391
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
# These methods implement the HTML visitor.
|
|
392
|
+
##
|
|
393
|
+
# Prepares the visitor for HTML generation
|
|
277
394
|
|
|
278
|
-
|
|
279
|
-
|
|
395
|
+
def start_accepting
|
|
396
|
+
@res = []
|
|
397
|
+
@in_list_entry = []
|
|
398
|
+
@list = []
|
|
399
|
+
@heading_ids = {}
|
|
400
|
+
end
|
|
280
401
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
@in_list_entry = []
|
|
284
|
-
@list = []
|
|
285
|
-
@heading_ids = {}
|
|
286
|
-
end
|
|
402
|
+
##
|
|
403
|
+
# Returns the generated output
|
|
287
404
|
|
|
288
|
-
|
|
289
|
-
|
|
405
|
+
def end_accepting
|
|
406
|
+
@res.join
|
|
407
|
+
end
|
|
290
408
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
end
|
|
409
|
+
##
|
|
410
|
+
# Adds +block_quote+ to the output
|
|
294
411
|
|
|
295
|
-
|
|
296
|
-
|
|
412
|
+
def accept_block_quote(block_quote)
|
|
413
|
+
@res << "\n<blockquote>"
|
|
297
414
|
|
|
298
|
-
|
|
299
|
-
|
|
415
|
+
block_quote.parts.each do |part|
|
|
416
|
+
part.accept self
|
|
417
|
+
end
|
|
300
418
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
end
|
|
419
|
+
@res << "</blockquote>\n"
|
|
420
|
+
end
|
|
304
421
|
|
|
305
|
-
|
|
306
|
-
|
|
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
|
|
307
434
|
|
|
308
|
-
|
|
309
|
-
# Adds +paragraph+ to the output
|
|
310
|
-
|
|
311
|
-
def accept_paragraph(paragraph)
|
|
312
|
-
@res << "\n<p>"
|
|
313
|
-
text = paragraph.text @hard_break
|
|
314
|
-
text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K\r?\n(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
|
|
315
|
-
defined?($2) && ' '
|
|
316
|
-
}
|
|
317
|
-
@res << to_html(text)
|
|
318
|
-
@res << "</p>\n"
|
|
319
|
-
end
|
|
435
|
+
# Generate syntax highlighted html for ruby-like text.
|
|
320
436
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
klass = nil
|
|
329
|
-
|
|
330
|
-
# Apply Ruby syntax highlighting if
|
|
331
|
-
# - explicitly marked as Ruby (via ruby? which accepts :ruby or :rb)
|
|
332
|
-
# - no format specified but the text is parseable as Ruby
|
|
333
|
-
# Otherwise, add language class when applicable and skip Ruby highlighting
|
|
334
|
-
content = if verbatim.ruby? || (format.nil? && parseable?(text))
|
|
335
|
-
begin
|
|
336
|
-
tokens = RDoc::Parser::RipperStateLex.parse text
|
|
337
|
-
klass = ' class="ruby"'
|
|
338
|
-
|
|
339
|
-
result = RDoc::TokenStream.to_html tokens
|
|
340
|
-
result = result + "\n" unless "\n" == result[-1]
|
|
341
|
-
result
|
|
342
|
-
rescue
|
|
343
|
-
CGI.escapeHTML text
|
|
344
|
-
end
|
|
345
|
-
else
|
|
346
|
-
klass = " class=\"#{format}\"" if format
|
|
347
|
-
CGI.escapeHTML text
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
if @options.pipe then
|
|
351
|
-
@res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
|
|
352
|
-
else
|
|
353
|
-
@res << "\n<pre#{klass}>#{content}</pre>\n"
|
|
354
|
-
end
|
|
355
|
-
end
|
|
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
|
|
356
443
|
|
|
357
|
-
|
|
358
|
-
|
|
444
|
+
##
|
|
445
|
+
# Adds +verbatim+ to the output
|
|
446
|
+
|
|
447
|
+
def accept_verbatim(verbatim)
|
|
448
|
+
text = verbatim.text.rstrip
|
|
449
|
+
format = verbatim.format
|
|
450
|
+
|
|
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
|
|
359
462
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
|
363
469
|
|
|
364
|
-
|
|
365
|
-
|
|
470
|
+
##
|
|
471
|
+
# Adds +rule+ to the output
|
|
366
472
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
@in_list_entry.push false
|
|
371
|
-
end
|
|
473
|
+
def accept_rule(rule)
|
|
474
|
+
@res << "<hr>\n"
|
|
475
|
+
end
|
|
372
476
|
|
|
373
|
-
|
|
374
|
-
|
|
477
|
+
##
|
|
478
|
+
# Prepares the visitor for consuming +list+
|
|
375
479
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@res << html_list_name(list.type, false) << "\n"
|
|
382
|
-
end
|
|
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
|
|
383
485
|
|
|
384
|
-
|
|
385
|
-
|
|
486
|
+
##
|
|
487
|
+
# Finishes consumption of +list+
|
|
386
488
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
391
496
|
|
|
392
|
-
|
|
393
|
-
|
|
497
|
+
##
|
|
498
|
+
# Prepares the visitor for consuming +list_item+
|
|
394
499
|
|
|
395
|
-
|
|
396
|
-
|
|
500
|
+
def accept_list_item_start(list_item)
|
|
501
|
+
if tag = @in_list_entry.last
|
|
502
|
+
@res << tag
|
|
503
|
+
end
|
|
397
504
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
end
|
|
505
|
+
@res << list_item_start(list_item, @list.last)
|
|
506
|
+
end
|
|
401
507
|
|
|
402
|
-
|
|
403
|
-
|
|
508
|
+
##
|
|
509
|
+
# Finishes consumption of +list_item+
|
|
404
510
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
511
|
+
def accept_list_item_end(list_item)
|
|
512
|
+
@in_list_entry[-1] = list_end_for(@list.last)
|
|
513
|
+
end
|
|
408
514
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
# level 6.
|
|
515
|
+
##
|
|
516
|
+
# Adds +blank_line+ to the output
|
|
412
517
|
|
|
413
|
-
|
|
414
|
-
|
|
518
|
+
def accept_blank_line(blank_line)
|
|
519
|
+
# @res << annotate("<p />") << "\n"
|
|
520
|
+
end
|
|
415
521
|
|
|
416
|
-
|
|
417
|
-
|
|
522
|
+
##
|
|
523
|
+
# Adds +heading+ to the output. The headings greater than 6 are trimmed to
|
|
524
|
+
# level 6.
|
|
418
525
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if @options.output_decoration && !@options.pipe
|
|
422
|
-
@res << "\n<span id=\"#{legacy_label}\" class=\"legacy-anchor\"></span>"
|
|
423
|
-
end
|
|
526
|
+
def accept_heading(heading)
|
|
527
|
+
level = [6, heading.level].min
|
|
424
528
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
else
|
|
428
|
-
"\n<h#{level}>"
|
|
429
|
-
end
|
|
529
|
+
label = deduplicate_heading_id(heading.label(@code_object))
|
|
530
|
+
legacy_label = deduplicate_heading_id(heading.legacy_label(@code_object))
|
|
430
531
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
537
|
|
|
437
|
-
|
|
438
|
-
|
|
538
|
+
@res << if @output_decoration
|
|
539
|
+
"\n<h#{level} id=\"#{label}\">"
|
|
540
|
+
else
|
|
541
|
+
"\n<h#{level}>"
|
|
542
|
+
end
|
|
439
543
|
|
|
440
|
-
|
|
441
|
-
|
|
544
|
+
if @pipe
|
|
545
|
+
@res << to_html(heading.text)
|
|
546
|
+
else
|
|
547
|
+
@res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
|
|
548
|
+
end
|
|
442
549
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
end
|
|
550
|
+
@res << "</h#{level}>\n"
|
|
551
|
+
end
|
|
446
552
|
|
|
447
|
-
|
|
448
|
-
|
|
553
|
+
##
|
|
554
|
+
# Adds +raw+ to the output
|
|
449
555
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
@res << '<th'
|
|
454
|
-
@res << ' align="' << align << '"' if align
|
|
455
|
-
@res << '>' << to_html(text) << "</th>\n"
|
|
456
|
-
end
|
|
457
|
-
@res << "</tr>\n</thead>\n<tbody>\n"
|
|
458
|
-
body.each do |row|
|
|
459
|
-
@res << "<tr>\n"
|
|
460
|
-
row.zip(aligns) do |text, align|
|
|
461
|
-
@res << '<td'
|
|
462
|
-
@res << ' align="' << align << '"' if align
|
|
463
|
-
@res << '>' << to_html(text) << "</td>\n"
|
|
464
|
-
end
|
|
465
|
-
@res << "</tr>\n"
|
|
466
|
-
end
|
|
467
|
-
@res << "</tbody>\n</table>\n"
|
|
468
|
-
end
|
|
556
|
+
def accept_raw(raw)
|
|
557
|
+
@res << raw.parts.join("\n")
|
|
558
|
+
end
|
|
469
559
|
|
|
470
|
-
|
|
560
|
+
##
|
|
561
|
+
# Adds +table+ to the output
|
|
471
562
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
|
475
582
|
|
|
476
|
-
|
|
477
|
-
if @heading_ids.key?(id)
|
|
478
|
-
@heading_ids[id] += 1
|
|
479
|
-
"#{id}-#{@heading_ids[id]}"
|
|
480
|
-
else
|
|
481
|
-
@heading_ids[id] = 0
|
|
482
|
-
id
|
|
483
|
-
end
|
|
484
|
-
end
|
|
583
|
+
# :section: Utilities
|
|
485
584
|
|
|
486
|
-
|
|
487
|
-
|
|
585
|
+
##
|
|
586
|
+
# Returns a unique heading ID, appending -1, -2, etc. for duplicates.
|
|
587
|
+
# Matches GitHub's behavior for duplicate heading anchors.
|
|
488
588
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
|
597
|
+
end
|
|
492
598
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
#
|
|
496
|
-
# - Image URLs (http/https/link ending in .gif, .png, .jpg, .jpeg, .bmp)
|
|
497
|
-
# become <img> tags
|
|
498
|
-
# - File references (.rb, .rdoc, .md) are converted to .html paths
|
|
499
|
-
# - Anchor URLs (#foo) pass through unchanged for GitHub-style header linking
|
|
500
|
-
# - Footnote links get wrapped in <sup> tags
|
|
599
|
+
##
|
|
600
|
+
# CGI-escapes +text+
|
|
501
601
|
|
|
502
|
-
|
|
503
|
-
|
|
602
|
+
def convert_string(text)
|
|
603
|
+
CGI.escapeHTML text
|
|
604
|
+
end
|
|
504
605
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
|
629
|
+
|
|
630
|
+
text = text.sub %r%^#{scheme}:/*%i, ''
|
|
631
|
+
text = text.sub %r%^[*\^](\d+)$%, '\1'
|
|
632
|
+
|
|
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
|
|
510
641
|
end
|
|
511
642
|
|
|
512
|
-
|
|
513
|
-
|
|
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
|
|
514
655
|
|
|
515
|
-
|
|
656
|
+
##
|
|
657
|
+
# Determines the HTML list element for +list_type+ and +open_tag+
|
|
516
658
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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]
|
|
521
663
|
end
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
664
|
|
|
525
|
-
|
|
526
|
-
|
|
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
|
|
527
681
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
682
|
+
##
|
|
683
|
+
# Returns the HTML end-tag for +list_type+
|
|
684
|
+
|
|
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
|
|
533
695
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
# +list_item+
|
|
537
|
-
|
|
538
|
-
def list_item_start(list_item, list_type)
|
|
539
|
-
case list_type
|
|
540
|
-
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
|
|
541
|
-
"<li>"
|
|
542
|
-
when :LABEL, :NOTE then
|
|
543
|
-
Array(list_item.label).map do |label|
|
|
544
|
-
"<dt>#{to_html label}</dt>\n"
|
|
545
|
-
end.join << "<dd>"
|
|
546
|
-
else
|
|
547
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
|
|
548
|
-
end
|
|
549
|
-
end
|
|
696
|
+
##
|
|
697
|
+
# Returns true if text is valid ruby syntax
|
|
550
698
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
def list_end_for(list_type)
|
|
555
|
-
case list_type
|
|
556
|
-
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
|
|
557
|
-
"</li>"
|
|
558
|
-
when :LABEL, :NOTE then
|
|
559
|
-
"</dd>"
|
|
560
|
-
else
|
|
561
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
|
|
562
|
-
end
|
|
563
|
-
end
|
|
699
|
+
def parseable?(text)
|
|
700
|
+
Prism.parse_success?(text)
|
|
701
|
+
end
|
|
564
702
|
|
|
565
|
-
|
|
566
|
-
|
|
703
|
+
##
|
|
704
|
+
# Converts +item+ to HTML using RDoc::Text#to_html
|
|
567
705
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
eval("BEGIN { throw :valid, true }\n#{text}")
|
|
706
|
+
def to_html(item)
|
|
707
|
+
handle_inline(item)
|
|
708
|
+
end
|
|
572
709
|
end
|
|
573
|
-
rescue SyntaxError
|
|
574
|
-
false
|
|
575
|
-
ensure
|
|
576
|
-
$VERBOSE = verbose
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
##
|
|
580
|
-
# Converts +item+ to HTML using RDoc::Text#to_html
|
|
581
|
-
|
|
582
|
-
def to_html(item)
|
|
583
|
-
# Ideally, we should convert html characters at handle_PLAIN_TEXT or somewhere else,
|
|
584
|
-
# but we need to convert it here for now because to_html_characters converts pair of backticks to ’‘ and pair of double backticks to ”“.
|
|
585
|
-
# Known bugs: `...` in `<code>def f(...); end</code>` and `(c) in `<a href="(c)">` will be wrongly converted.
|
|
586
|
-
to_html_characters(handle_inline(item))
|
|
587
|
-
end
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
##
|
|
591
|
-
# Formatter dedicated to rendering tidy link labels without mutating the
|
|
592
|
-
# calling formatter's state.
|
|
593
|
-
|
|
594
|
-
class RDoc::Markup::LinkLabelToHtml < RDoc::Markup::ToHtml
|
|
595
|
-
def self.render(label, options, from_path)
|
|
596
|
-
new(options, from_path).to_html(label)
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
def initialize(options, from_path = nil)
|
|
600
|
-
super(options)
|
|
601
|
-
|
|
602
|
-
self.from_path = from_path if from_path
|
|
603
710
|
end
|
|
604
711
|
end
|