rdoc 6.17.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 +193 -0
- data/LEGAL.rdoc +6 -0
- data/LICENSE.rdoc +4 -0
- data/README.md +132 -8
- data/RI.md +75 -75
- data/doc/markup_reference/markdown.md +659 -0
- data/doc/markup_reference/rdoc.rdoc +1169 -0
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +70 -74
- data/lib/rdoc/code_object/any_method.rb +308 -293
- data/lib/rdoc/code_object/attr.rb +150 -142
- data/lib/rdoc/code_object/class_module.rb +802 -715
- data/lib/rdoc/code_object/constant.rb +181 -144
- data/lib/rdoc/code_object/context/section.rb +140 -130
- data/lib/rdoc/code_object/context.rb +925 -941
- 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 +328 -315
- data/lib/rdoc/code_object/mixin.rb +97 -92
- 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 +215 -202
- data/lib/rdoc/code_object.rb +305 -305
- data/lib/rdoc/comment.rb +274 -337
- data/lib/rdoc/cross_reference.rb +195 -206
- 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 +196 -30
- data/lib/rdoc/generator/darkfish.rb +647 -629
- 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 +2 -2
- data/lib/rdoc/generator/template/aliki/_head.rhtml +15 -10
- 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 +3 -3
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +3 -3
- 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 +71 -61
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +796 -323
- data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/js/aliki.js +119 -113
- data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
- data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
- data/lib/rdoc/generator/template/aliki/js/{search.js → search_controller.js} +16 -7
- data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
- data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
- 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 +2 -2
- 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 +29 -32
- data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -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 +52 -33
- data/lib/rdoc/markdown.rb +365 -177
- data/lib/rdoc/markup/blank_line.rb +25 -23
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/element.rb +21 -0
- data/lib/rdoc/markup/formatter.rb +220 -203
- data/lib/rdoc/markup/hard_break.rb +30 -27
- data/lib/rdoc/markup/heading.rb +163 -77
- data/lib/rdoc/markup/include.rb +33 -29
- data/lib/rdoc/markup/indented_paragraph.rb +37 -33
- data/lib/rdoc/markup/inline_parser.rb +316 -0
- 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 +52 -55
- data/lib/rdoc/markup/rule.rb +16 -12
- data/lib/rdoc/markup/table.rb +48 -40
- data/lib/rdoc/markup/to_ansi.rb +143 -92
- data/lib/rdoc/markup/to_bs.rb +86 -102
- data/lib/rdoc/markup/to_html.rb +608 -506
- data/lib/rdoc/markup/to_html_crossref.rb +219 -210
- data/lib/rdoc/markup/to_html_snippet.rb +210 -205
- data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
- data/lib/rdoc/markup/to_label.rb +64 -51
- data/lib/rdoc/markup/to_markdown.rb +212 -184
- data/lib/rdoc/markup/to_rdoc.rb +341 -277
- data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
- data/lib/rdoc/markup/to_test.rb +62 -50
- data/lib/rdoc/markup/to_tt_only.rb +83 -92
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +196 -215
- data/lib/rdoc/options.rb +1063 -1076
- data/lib/rdoc/parser/c.rb +1039 -1036
- data/lib/rdoc/parser/changelog.rb +320 -287
- 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 +164 -240
- data/lib/rdoc/token_stream.rb +88 -106
- 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 +9 -7
- metadata +46 -32
- data/CONTRIBUTING.rdoc +0 -219
- data/ExampleMarkdown.md +0 -39
- data/ExampleRDoc.rdoc +0 -210
- 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/markup/attr_changer.rb +0 -22
- data/lib/rdoc/markup/attr_span.rb +0 -35
- data/lib/rdoc/markup/attribute_manager.rb +0 -405
- data/lib/rdoc/markup/attributes.rb +0 -70
- data/lib/rdoc/markup/regexp_handling.rb +0 -40
- data/lib/rdoc/parser/prism_ruby.rb +0 -1100
- 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,609 +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
|
-
|
|
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
|
-
# The RDoc::CodeObject HTML is being generated for. This is used to
|
|
32
|
-
# generate namespaced URI fragments
|
|
33
|
-
|
|
34
|
-
attr_accessor :code_object
|
|
35
|
-
|
|
36
|
-
##
|
|
37
|
-
# Path to this document for relative links
|
|
38
|
-
|
|
39
|
-
attr_accessor :from_path
|
|
40
|
-
|
|
41
|
-
# :section:
|
|
42
|
-
|
|
43
|
-
##
|
|
44
|
-
# Creates a new formatter that will output HTML
|
|
45
|
-
|
|
46
|
-
def initialize(options, markup = nil)
|
|
47
|
-
super
|
|
48
|
-
|
|
49
|
-
@code_object = nil
|
|
50
|
-
@from_path = ''
|
|
51
|
-
@in_list_entry = nil
|
|
52
|
-
@list = nil
|
|
53
|
-
@th = nil
|
|
54
|
-
@hard_break = "<br>\n"
|
|
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
|
|
55
90
|
|
|
56
|
-
|
|
91
|
+
# Converts ascii quote pairs to multibyte quote characters
|
|
92
|
+
class QuoteConverter
|
|
93
|
+
|
|
94
|
+
def initialize
|
|
95
|
+
@in_dquote = false
|
|
96
|
+
@in_squote = false
|
|
97
|
+
end
|
|
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
|
|
124
|
+
end
|
|
125
|
+
end
|
|
57
126
|
|
|
58
|
-
|
|
59
|
-
|
|
127
|
+
##
|
|
128
|
+
# Creates a new formatter that will output HTML
|
|
60
129
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
130
|
+
def initialize(pipe: false, output_decoration: true)
|
|
131
|
+
super()
|
|
64
132
|
|
|
65
|
-
|
|
66
|
-
|
|
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"
|
|
67
143
|
|
|
68
|
-
|
|
69
|
-
|
|
144
|
+
init_regexp_handlings
|
|
145
|
+
end
|
|
70
146
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
:HYPERLINK)
|
|
75
|
-
init_link_notation_regexp_handlings
|
|
76
|
-
end
|
|
147
|
+
# :section: Regexp Handling
|
|
148
|
+
#
|
|
149
|
+
# These methods are used by regexp handling markup added by RDoc::Markup#add_regexp_handling.
|
|
77
150
|
|
|
78
|
-
|
|
79
|
-
|
|
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.
|
|
80
154
|
|
|
81
|
-
|
|
82
|
-
add_regexp_handling_RDOCLINK
|
|
83
|
-
add_regexp_handling_TIDYLINK
|
|
84
|
-
end
|
|
155
|
+
UNSAFE_URL_SCHEME_REGEXP = /\A(?:javascript|vbscript|data|file):/i
|
|
85
156
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
when /^rdoc-ref:/
|
|
89
|
-
CGI.escapeHTML($')
|
|
90
|
-
when /^rdoc-label:/
|
|
91
|
-
text = $'
|
|
92
|
-
|
|
93
|
-
text = case text
|
|
94
|
-
when /\Alabel-/ then $'
|
|
95
|
-
when /\Afootmark-/ then $'
|
|
96
|
-
when /\Afoottext-/ then $'
|
|
97
|
-
else text
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
gen_url CGI.escapeHTML(url), CGI.escapeHTML(text)
|
|
101
|
-
when /^rdoc-image:/
|
|
102
|
-
# Split the string after "rdoc-image:" into url and alt.
|
|
103
|
-
# "path/to/image.jpg:alt text" => ["path/to/image.jpg", "alt text"]
|
|
104
|
-
# "http://example.com/path/to/image.jpg:alt text" => ["http://example.com/path/to/image.jpg", "alt text"]
|
|
105
|
-
url, alt = $'.split(/:(?!\/)/, 2)
|
|
106
|
-
if alt && !alt.empty?
|
|
107
|
-
%[<img src="#{CGI.escapeHTML(url)}" alt="#{CGI.escapeHTML(alt)}">]
|
|
108
|
-
else
|
|
109
|
-
%[<img src="#{CGI.escapeHTML(url)}">]
|
|
110
|
-
end
|
|
111
|
-
when /\Ardoc-[a-z]+:/
|
|
112
|
-
CGI.escapeHTML($')
|
|
113
|
-
end
|
|
114
|
-
end
|
|
157
|
+
# :nodoc:
|
|
158
|
+
URL_CHARACTERS_REGEXP_STR = /[A-Za-z0-9\-._~:\/\?#\[\]@!$&'\(\)*+,;%=]/.source
|
|
115
159
|
|
|
116
|
-
|
|
117
|
-
|
|
160
|
+
##
|
|
161
|
+
# Adds regexp handlings.
|
|
118
162
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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)
|
|
122
167
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
# <tt>mailto:</tt>::
|
|
127
|
-
# Inserted as-is.
|
|
128
|
-
# <tt>http:</tt>::
|
|
129
|
-
# Links are checked to see if they reference an image. If so, that image
|
|
130
|
-
# gets inserted using an <tt><img></tt> tag. Otherwise a conventional
|
|
131
|
-
# <tt><a href></tt> is used.
|
|
132
|
-
# <tt>link:</tt>::
|
|
133
|
-
# Reference to a local file relative to the output directory.
|
|
134
|
-
|
|
135
|
-
def handle_regexp_HYPERLINK(target)
|
|
136
|
-
url = CGI.escapeHTML(target.text)
|
|
137
|
-
|
|
138
|
-
gen_url url, url
|
|
139
|
-
end
|
|
168
|
+
# suppress crossref: \#method \::method \ClassName \method_with_underscores
|
|
169
|
+
@markup.add_regexp_handling(/\\(?:[#:A-Z]|[a-z]+_[a-z0-9])/, :SUPPRESSED_CROSSREF)
|
|
140
170
|
|
|
141
|
-
|
|
142
|
-
# +target+ is an rdoc-schemed link that will be converted into a hyperlink.
|
|
143
|
-
#
|
|
144
|
-
# For the +rdoc-ref+ scheme the named reference will be returned without
|
|
145
|
-
# creating a link.
|
|
146
|
-
#
|
|
147
|
-
# For the +rdoc-label+ scheme the footnote and label prefixes are stripped
|
|
148
|
-
# when creating a link. All other contents will be linked verbatim.
|
|
149
|
-
|
|
150
|
-
def handle_regexp_RDOCLINK(target)
|
|
151
|
-
handle_RDOCLINK target.text
|
|
152
|
-
end
|
|
171
|
+
@markup.add_regexp_handling(HTML_CHARACTERS_REGEXP, :HTML_CHARACTERS)
|
|
153
172
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
# <tt>label[url]</tt> or <tt>{long label}[url]</tt>
|
|
173
|
+
@markup.add_regexp_handling(/\b['"`]/, :QUOTE_AFTER_WORD)
|
|
174
|
+
@markup.add_regexp_handling(/\B['"`]/, :QUOTE_NOT_AFTER_WORD)
|
|
157
175
|
|
|
158
|
-
|
|
159
|
-
|
|
176
|
+
init_link_notation_regexp_handlings
|
|
177
|
+
end
|
|
160
178
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
end
|
|
179
|
+
##
|
|
180
|
+
# Adds regexp handlings about link notations.
|
|
164
181
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
end
|
|
182
|
+
def init_link_notation_regexp_handlings
|
|
183
|
+
add_regexp_handling_RDOCLINK
|
|
184
|
+
end
|
|
169
185
|
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
172
200
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
176
216
|
|
|
177
|
-
|
|
178
|
-
|
|
217
|
+
def handle_PLAIN_TEXT(text)
|
|
218
|
+
emit_inline(convert_string(text))
|
|
219
|
+
end
|
|
179
220
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@list = []
|
|
184
|
-
end
|
|
221
|
+
def handle_REGEXP_HANDLING_TEXT(text)
|
|
222
|
+
emit_inline(text)
|
|
223
|
+
end
|
|
185
224
|
|
|
186
|
-
|
|
187
|
-
|
|
225
|
+
def handle_BOLD(nodes)
|
|
226
|
+
emit_inline('<strong>')
|
|
227
|
+
super
|
|
228
|
+
emit_inline('</strong>')
|
|
229
|
+
end
|
|
188
230
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
231
|
+
def handle_EM(nodes)
|
|
232
|
+
emit_inline('<em>')
|
|
233
|
+
super
|
|
234
|
+
emit_inline('</em>')
|
|
235
|
+
end
|
|
192
236
|
|
|
193
|
-
|
|
194
|
-
|
|
237
|
+
def handle_BOLD_WORD(word)
|
|
238
|
+
emit_inline('<strong>')
|
|
239
|
+
super
|
|
240
|
+
emit_inline('</strong>')
|
|
241
|
+
end
|
|
195
242
|
|
|
196
|
-
|
|
197
|
-
|
|
243
|
+
def handle_EM_WORD(word)
|
|
244
|
+
emit_inline('<em>')
|
|
245
|
+
super
|
|
246
|
+
emit_inline('</em>')
|
|
247
|
+
end
|
|
198
248
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
249
|
+
def handle_TT(code)
|
|
250
|
+
emit_inline('<code>')
|
|
251
|
+
super
|
|
252
|
+
emit_inline('</code>')
|
|
253
|
+
end
|
|
202
254
|
|
|
203
|
-
|
|
204
|
-
|
|
255
|
+
def handle_STRIKE(nodes)
|
|
256
|
+
emit_inline('<del>')
|
|
257
|
+
super
|
|
258
|
+
emit_inline('</del>')
|
|
259
|
+
end
|
|
205
260
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def accept_paragraph(paragraph)
|
|
210
|
-
@res << "\n<p>"
|
|
211
|
-
text = paragraph.text @hard_break
|
|
212
|
-
text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K\r?\n(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
|
|
213
|
-
defined?($2) && ' '
|
|
214
|
-
}
|
|
215
|
-
@res << to_html(text)
|
|
216
|
-
@res << "</p>\n"
|
|
217
|
-
end
|
|
261
|
+
def handle_HARD_BREAK
|
|
262
|
+
emit_inline('<br>')
|
|
263
|
+
end
|
|
218
264
|
|
|
219
|
-
|
|
220
|
-
|
|
265
|
+
def emit_inline(text)
|
|
266
|
+
@inline_output << text
|
|
267
|
+
end
|
|
221
268
|
|
|
222
|
-
|
|
223
|
-
text = verbatim.text.rstrip
|
|
269
|
+
# Returns true if we are processing inside a tidy link label.
|
|
224
270
|
|
|
225
|
-
|
|
271
|
+
def in_tidylink_label?
|
|
272
|
+
@in_tidylink_label
|
|
273
|
+
end
|
|
226
274
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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.
|
|
231
281
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
rescue
|
|
236
|
-
CGI.escapeHTML text
|
|
237
|
-
end
|
|
238
|
-
else
|
|
239
|
-
CGI.escapeHTML text
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
if @options.pipe then
|
|
243
|
-
@res << "\n<pre><code>#{CGI.escapeHTML text}\n</code></pre>\n"
|
|
244
|
-
else
|
|
245
|
-
@res << "\n<pre#{klass}>#{content}</pre>\n"
|
|
246
|
-
end
|
|
247
|
-
end
|
|
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/)
|
|
248
285
|
|
|
249
|
-
|
|
250
|
-
|
|
286
|
+
# rdoc-image in label specially allows spaces in alt text.
|
|
287
|
+
return handle_RDOCLINK(label) if label.start_with?('rdoc-image:')
|
|
288
|
+
end
|
|
251
289
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
299
|
+
|
|
300
|
+
if label_part.size == 1 && String === label_part[0]
|
|
301
|
+
raw_label = label_part[0]
|
|
302
|
+
|
|
303
|
+
@in_tidylink_label = true
|
|
304
|
+
special = apply_tidylink_label_special_handling(raw_label, url)
|
|
305
|
+
@in_tidylink_label = false
|
|
306
|
+
|
|
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
|
|
315
|
+
|
|
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
|
|
255
325
|
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
258
335
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|
264
341
|
|
|
265
|
-
|
|
266
|
-
|
|
342
|
+
def handle_regexp_QUOTE_NOT_AFTER_WORD(text)
|
|
343
|
+
@quote_converter.convert(text, after_word: false) || convert_string(text)
|
|
344
|
+
end
|
|
267
345
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
@res << tag
|
|
272
|
-
end
|
|
273
|
-
@res << html_list_name(list.type, false) << "\n"
|
|
274
|
-
end
|
|
346
|
+
def handle_regexp_QUOTE_AFTER_WORD(text)
|
|
347
|
+
@quote_converter.convert(text, after_word: true) || convert_string(text)
|
|
348
|
+
end
|
|
275
349
|
|
|
276
|
-
|
|
277
|
-
# Prepares the visitor for consuming +list_item+
|
|
350
|
+
# Converts suppressed cross-reference +text+ to HTML by removing the leading backslash.
|
|
278
351
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
end
|
|
352
|
+
def handle_regexp_SUPPRESSED_CROSSREF(text)
|
|
353
|
+
convert_string(text.delete_prefix('\\'))
|
|
354
|
+
end
|
|
283
355
|
|
|
284
|
-
|
|
285
|
-
|
|
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
|
|
286
374
|
|
|
287
|
-
|
|
288
|
-
|
|
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
|
|
289
387
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
388
|
+
# :section: Visitor
|
|
389
|
+
#
|
|
390
|
+
# These methods implement the HTML visitor.
|
|
293
391
|
|
|
294
|
-
|
|
295
|
-
|
|
392
|
+
##
|
|
393
|
+
# Prepares the visitor for HTML generation
|
|
296
394
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
395
|
+
def start_accepting
|
|
396
|
+
@res = []
|
|
397
|
+
@in_list_entry = []
|
|
398
|
+
@list = []
|
|
399
|
+
@heading_ids = {}
|
|
400
|
+
end
|
|
300
401
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
# level 6.
|
|
402
|
+
##
|
|
403
|
+
# Returns the generated output
|
|
304
404
|
|
|
305
|
-
|
|
306
|
-
|
|
405
|
+
def end_accepting
|
|
406
|
+
@res.join
|
|
407
|
+
end
|
|
307
408
|
|
|
308
|
-
|
|
409
|
+
##
|
|
410
|
+
# Adds +block_quote+ to the output
|
|
309
411
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
else
|
|
313
|
-
"\n<h#{level}>"
|
|
314
|
-
end
|
|
412
|
+
def accept_block_quote(block_quote)
|
|
413
|
+
@res << "\n<blockquote>"
|
|
315
414
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
@res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
|
|
320
|
-
end
|
|
415
|
+
block_quote.parts.each do |part|
|
|
416
|
+
part.accept self
|
|
417
|
+
end
|
|
321
418
|
|
|
322
|
-
|
|
323
|
-
|
|
419
|
+
@res << "</blockquote>\n"
|
|
420
|
+
end
|
|
324
421
|
|
|
325
|
-
|
|
326
|
-
|
|
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
|
|
327
434
|
|
|
328
|
-
|
|
329
|
-
@res << raw.parts.join("\n")
|
|
330
|
-
end
|
|
435
|
+
# Generate syntax highlighted html for ruby-like text.
|
|
331
436
|
|
|
332
|
-
|
|
333
|
-
|
|
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
|
|
334
443
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
|
462
|
+
|
|
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
|
|
354
469
|
|
|
355
|
-
|
|
470
|
+
##
|
|
471
|
+
# Adds +rule+ to the output
|
|
356
472
|
|
|
357
|
-
|
|
358
|
-
|
|
473
|
+
def accept_rule(rule)
|
|
474
|
+
@res << "<hr>\n"
|
|
475
|
+
end
|
|
359
476
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
end
|
|
477
|
+
##
|
|
478
|
+
# Prepares the visitor for consuming +list+
|
|
363
479
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
|
367
485
|
|
|
368
|
-
|
|
369
|
-
|
|
486
|
+
##
|
|
487
|
+
# Finishes consumption of +list+
|
|
370
488
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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"
|
|
377
495
|
end
|
|
378
496
|
|
|
379
|
-
|
|
380
|
-
|
|
497
|
+
##
|
|
498
|
+
# Prepares the visitor for consuming +list_item+
|
|
381
499
|
|
|
382
|
-
|
|
500
|
+
def accept_list_item_start(list_item)
|
|
501
|
+
if tag = @in_list_entry.last
|
|
502
|
+
@res << tag
|
|
503
|
+
end
|
|
383
504
|
|
|
384
|
-
|
|
505
|
+
@res << list_item_start(list_item, @list.last)
|
|
506
|
+
end
|
|
385
507
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
end
|
|
508
|
+
##
|
|
509
|
+
# Finishes consumption of +list_item+
|
|
389
510
|
|
|
390
|
-
|
|
391
|
-
|
|
511
|
+
def accept_list_item_end(list_item)
|
|
512
|
+
@in_list_entry[-1] = list_end_for(@list.last)
|
|
513
|
+
end
|
|
392
514
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
|
|
396
|
-
tags[open_tag ? 0 : 1]
|
|
397
|
-
end
|
|
515
|
+
##
|
|
516
|
+
# Adds +blank_line+ to the output
|
|
398
517
|
|
|
399
|
-
|
|
400
|
-
|
|
518
|
+
def accept_blank_line(blank_line)
|
|
519
|
+
# @res << annotate("<p />") << "\n"
|
|
520
|
+
end
|
|
401
521
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
add_tag :EM, "<em>", "</em>"
|
|
406
|
-
end
|
|
522
|
+
##
|
|
523
|
+
# Adds +heading+ to the output. The headings greater than 6 are trimmed to
|
|
524
|
+
# level 6.
|
|
407
525
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
# +list_item+
|
|
411
|
-
|
|
412
|
-
def list_item_start(list_item, list_type)
|
|
413
|
-
case list_type
|
|
414
|
-
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
|
|
415
|
-
"<li>"
|
|
416
|
-
when :LABEL, :NOTE then
|
|
417
|
-
Array(list_item.label).map do |label|
|
|
418
|
-
"<dt>#{to_html label}</dt>\n"
|
|
419
|
-
end.join << "<dd>"
|
|
420
|
-
else
|
|
421
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
|
|
422
|
-
end
|
|
423
|
-
end
|
|
526
|
+
def accept_heading(heading)
|
|
527
|
+
level = [6, heading.level].min
|
|
424
528
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
def list_end_for(list_type)
|
|
429
|
-
case list_type
|
|
430
|
-
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
|
|
431
|
-
"</li>"
|
|
432
|
-
when :LABEL, :NOTE then
|
|
433
|
-
"</dd>"
|
|
434
|
-
else
|
|
435
|
-
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
|
|
436
|
-
end
|
|
437
|
-
end
|
|
529
|
+
label = deduplicate_heading_id(heading.label(@code_object))
|
|
530
|
+
legacy_label = deduplicate_heading_id(heading.legacy_label(@code_object))
|
|
438
531
|
|
|
439
|
-
|
|
440
|
-
|
|
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
|
|
441
537
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
rescue SyntaxError
|
|
448
|
-
false
|
|
449
|
-
ensure
|
|
450
|
-
$VERBOSE = verbose
|
|
451
|
-
end
|
|
538
|
+
@res << if @output_decoration
|
|
539
|
+
"\n<h#{level} id=\"#{label}\">"
|
|
540
|
+
else
|
|
541
|
+
"\n<h#{level}>"
|
|
542
|
+
end
|
|
452
543
|
|
|
453
|
-
|
|
454
|
-
|
|
544
|
+
if @pipe
|
|
545
|
+
@res << to_html(heading.text)
|
|
546
|
+
else
|
|
547
|
+
@res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
|
|
548
|
+
end
|
|
455
549
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
end
|
|
550
|
+
@res << "</h#{level}>\n"
|
|
551
|
+
end
|
|
459
552
|
|
|
460
|
-
|
|
553
|
+
##
|
|
554
|
+
# Adds +raw+ to the output
|
|
461
555
|
|
|
462
|
-
|
|
463
|
-
|
|
556
|
+
def accept_raw(raw)
|
|
557
|
+
@res << raw.parts.join("\n")
|
|
558
|
+
end
|
|
464
559
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
560
|
+
##
|
|
561
|
+
# Adds +table+ to the output
|
|
562
|
+
|
|
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"
|
|
476
581
|
end
|
|
477
|
-
end
|
|
478
582
|
|
|
479
|
-
|
|
480
|
-
end
|
|
583
|
+
# :section: Utilities
|
|
481
584
|
|
|
482
|
-
|
|
483
|
-
|
|
585
|
+
##
|
|
586
|
+
# Returns a unique heading ID, appending -1, -2, etc. for duplicates.
|
|
587
|
+
# Matches GitHub's behavior for duplicate heading anchors.
|
|
484
588
|
|
|
485
|
-
|
|
486
|
-
|
|
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
|
|
487
598
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
end
|
|
599
|
+
##
|
|
600
|
+
# CGI-escapes +text+
|
|
491
601
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
# Capture 1: label contents.
|
|
496
|
-
# Capture 2: URL text.
|
|
497
|
-
# Capture 3: trailing content.
|
|
498
|
-
TIDY_LINK_WITH_BRACES = /\A\{(.*?)\}\[(.*?)\](.*)\z/
|
|
499
|
-
|
|
500
|
-
##
|
|
501
|
-
# Matches the tail of a braced tidy link when the opening brace was
|
|
502
|
-
# consumed earlier while accumulating the label text.
|
|
503
|
-
#
|
|
504
|
-
# Capture 1: remaining label content.
|
|
505
|
-
# Capture 2: URL text.
|
|
506
|
-
# Capture 3: trailing content.
|
|
507
|
-
TIDY_LINK_WITH_BRACES_TAIL = /\A(.*?)\}\[(.*?)\](.*)\z/
|
|
508
|
-
|
|
509
|
-
##
|
|
510
|
-
# Matches a tidy link with a single-word label "label[url]".
|
|
511
|
-
#
|
|
512
|
-
# Capture 1: the single-word label (no whitespace).
|
|
513
|
-
# Capture 2: URL text between the brackets.
|
|
514
|
-
TIDY_LINK_SINGLE_WORD = /\A(\S+)\[(.*?)\](.*)\z/
|
|
515
|
-
|
|
516
|
-
def convert_complete_tidy_link(text)
|
|
517
|
-
return text unless
|
|
518
|
-
text =~ TIDY_LINK_WITH_BRACES or text =~ TIDY_LINK_SINGLE_WORD
|
|
519
|
-
|
|
520
|
-
label = $1
|
|
521
|
-
url = CGI.escapeHTML($2)
|
|
522
|
-
|
|
523
|
-
label_html = if /^rdoc-image:/ =~ label
|
|
524
|
-
handle_RDOCLINK(label)
|
|
525
|
-
else
|
|
526
|
-
render_tidy_link_label(label)
|
|
527
|
-
end
|
|
602
|
+
def convert_string(text)
|
|
603
|
+
CGI.escapeHTML text
|
|
604
|
+
end
|
|
528
605
|
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
641
|
+
end
|
|
531
642
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
|
539
655
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
append_to_tidy_label CGI.escapeHTML(label_tail) unless label_tail.empty?
|
|
656
|
+
##
|
|
657
|
+
# Determines the HTML list element for +list_type+ and +open_tag+
|
|
543
658
|
|
|
544
|
-
|
|
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
|
|
545
664
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
|
549
681
|
|
|
550
|
-
|
|
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
|
|
551
695
|
|
|
552
|
-
|
|
553
|
-
|
|
696
|
+
##
|
|
697
|
+
# Returns true if text is valid ruby syntax
|
|
554
698
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
elsif text =~ TIDY_LINK_WITH_BRACES_TAIL
|
|
559
|
-
[$1, CGI.escapeHTML($2), $3]
|
|
560
|
-
elsif text =~ TIDY_LINK_SINGLE_WORD
|
|
561
|
-
[$1, CGI.escapeHTML($2), $3]
|
|
562
|
-
else
|
|
563
|
-
[text, nil, '']
|
|
564
|
-
end
|
|
565
|
-
end
|
|
699
|
+
def parseable?(text)
|
|
700
|
+
Prism.parse_success?(text)
|
|
701
|
+
end
|
|
566
702
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
emit_tidy_link_fragment(res, annotate(tag.on))
|
|
570
|
-
@in_tt += 1 if tt? tag
|
|
571
|
-
end
|
|
572
|
-
end
|
|
703
|
+
##
|
|
704
|
+
# Converts +item+ to HTML using RDoc::Text#to_html
|
|
573
705
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
@in_tt -= 1 if tt? tag
|
|
706
|
+
def to_html(item)
|
|
707
|
+
handle_inline(item)
|
|
708
|
+
end
|
|
578
709
|
end
|
|
579
710
|
end
|
|
580
|
-
|
|
581
|
-
def start_tidy_link(text)
|
|
582
|
-
@tidy_link_buffer = String.new
|
|
583
|
-
append_to_tidy_label CGI.escapeHTML(text.delete_prefix('{'))
|
|
584
|
-
end
|
|
585
|
-
|
|
586
|
-
def tidy_link_capturing?
|
|
587
|
-
!!@tidy_link_buffer
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
def render_tidy_link_label(label)
|
|
591
|
-
RDoc::Markup::LinkLabelToHtml.render(label, @options, @from_path)
|
|
592
|
-
end
|
|
593
|
-
end
|
|
594
|
-
|
|
595
|
-
##
|
|
596
|
-
# Formatter dedicated to rendering tidy link labels without mutating the
|
|
597
|
-
# calling formatter's state.
|
|
598
|
-
|
|
599
|
-
class RDoc::Markup::LinkLabelToHtml < RDoc::Markup::ToHtml
|
|
600
|
-
def self.render(label, options, from_path)
|
|
601
|
-
new(options, from_path).to_html(label)
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
def initialize(options, from_path = nil)
|
|
605
|
-
super(options)
|
|
606
|
-
|
|
607
|
-
self.from_path = from_path if from_path
|
|
608
|
-
end
|
|
609
711
|
end
|