rdoc 7.1.0 → 8.0.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 +3 -4
- data/LICENSE.rdoc +4 -0
- data/README.md +43 -2
- data/doc/markup_reference/markdown.md +104 -3
- data/lib/rdoc/code_object/alias.rb +2 -8
- data/lib/rdoc/code_object/any_method.rb +11 -6
- data/lib/rdoc/code_object/attr.rb +12 -6
- data/lib/rdoc/code_object/class_module.rb +62 -32
- data/lib/rdoc/code_object/constant.rb +29 -3
- data/lib/rdoc/code_object/context/section.rb +30 -43
- data/lib/rdoc/code_object/context.rb +53 -37
- data/lib/rdoc/code_object/method_attr.rb +9 -15
- data/lib/rdoc/code_object/mixin.rb +3 -0
- data/lib/rdoc/code_object/top_level.rb +11 -3
- data/lib/rdoc/code_object.rb +2 -4
- data/lib/rdoc/comment.rb +1 -66
- data/lib/rdoc/cross_reference.rb +8 -30
- data/lib/rdoc/encoding.rb +3 -3
- data/lib/rdoc/generator/aliki.rb +17 -0
- data/lib/rdoc/generator/darkfish.rb +12 -6
- data/lib/rdoc/generator/json_index.rb +2 -2
- data/lib/rdoc/generator/markup.rb +56 -31
- data/lib/rdoc/generator/template/aliki/DESIGN.md +536 -0
- data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +6 -1
- 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_sections.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/class.rhtml +61 -51
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +365 -147
- data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/js/aliki.js +28 -20
- data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
- data/lib/rdoc/generator/template/aliki/js/search_controller.js +1 -1
- 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/_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/i18n/text.rb +3 -3
- data/lib/rdoc/markdown.kpeg +36 -17
- data/lib/rdoc/markdown.rb +324 -125
- data/lib/rdoc/markup/document.rb +2 -2
- data/lib/rdoc/markup/formatter.rb +128 -115
- data/lib/rdoc/markup/heading.rb +3 -6
- data/lib/rdoc/markup/indented_paragraph.rb +1 -1
- data/lib/rdoc/markup/inline_parser.rb +312 -0
- data/lib/rdoc/markup/list.rb +2 -2
- data/lib/rdoc/markup/list_item.rb +2 -2
- data/lib/rdoc/markup/parser.rb +1 -1
- data/lib/rdoc/markup/pre_process.rb +0 -25
- data/lib/rdoc/markup/to_ansi.rb +52 -5
- data/lib/rdoc/markup/to_bs.rb +23 -43
- data/lib/rdoc/markup/to_html.rb +279 -226
- data/lib/rdoc/markup/to_html_crossref.rb +122 -136
- data/lib/rdoc/markup/to_html_snippet.rb +65 -65
- data/lib/rdoc/markup/to_joined_paragraph.rb +0 -5
- data/lib/rdoc/markup/to_label.rb +22 -23
- data/lib/rdoc/markup/to_markdown.rb +62 -38
- data/lib/rdoc/markup/to_rdoc.rb +88 -28
- data/lib/rdoc/markup/to_table_of_contents.rb +1 -1
- data/lib/rdoc/markup/to_test.rb +9 -1
- data/lib/rdoc/markup/to_tt_only.rb +10 -23
- data/lib/rdoc/markup/verbatim.rb +1 -1
- data/lib/rdoc/markup.rb +8 -30
- data/lib/rdoc/options.rb +36 -51
- data/lib/rdoc/parser/c.rb +7 -6
- data/lib/rdoc/parser/changelog.rb +21 -0
- data/lib/rdoc/parser/rbs.rb +275 -0
- data/lib/rdoc/parser/ruby.rb +955 -2067
- data/lib/rdoc/parser/ruby_colorizer.rb +253 -0
- data/lib/rdoc/parser.rb +3 -2
- data/lib/rdoc/rbs_helper.rb +186 -0
- data/lib/rdoc/rdoc.rb +196 -24
- data/lib/rdoc/ri/driver.rb +8 -2
- data/lib/rdoc/ri/paths.rb +1 -1
- data/lib/rdoc/{servlet.rb → ri/servlet.rb} +5 -5
- data/lib/rdoc/ri.rb +4 -3
- data/lib/rdoc/rubygems_hook.rb +11 -11
- data/lib/rdoc/server.rb +460 -0
- data/lib/rdoc/stats.rb +147 -124
- data/lib/rdoc/store.rb +212 -4
- data/lib/rdoc/task.rb +16 -15
- data/lib/rdoc/text.rb +24 -117
- data/lib/rdoc/token_stream.rb +11 -33
- 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 +37 -23
- 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/markup/attr_changer.rb +0 -22
- data/lib/rdoc/markup/attr_span.rb +0 -35
- data/lib/rdoc/markup/attribute_manager.rb +0 -432
- 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/markup/document.rb
CHANGED
|
@@ -26,7 +26,7 @@ class RDoc::Markup::Document
|
|
|
26
26
|
##
|
|
27
27
|
# Creates a new Document with +parts+
|
|
28
28
|
|
|
29
|
-
def initialize
|
|
29
|
+
def initialize(*parts)
|
|
30
30
|
@parts = []
|
|
31
31
|
@parts.concat parts
|
|
32
32
|
|
|
@@ -148,7 +148,7 @@ class RDoc::Markup::Document
|
|
|
148
148
|
##
|
|
149
149
|
# Appends +parts+ to the document
|
|
150
150
|
|
|
151
|
-
def push
|
|
151
|
+
def push(*parts)
|
|
152
152
|
self.parts.concat parts
|
|
153
153
|
end
|
|
154
154
|
|
|
@@ -10,13 +10,9 @@
|
|
|
10
10
|
# RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter
|
|
11
11
|
# use RDoc::Markup::TextFormatterTestCase which provides extra test cases.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
##
|
|
16
|
-
# Tag for inline markup containing a +bit+ for the bitmask and the +on+ and
|
|
17
|
-
# +off+ triggers.
|
|
13
|
+
require 'rdoc/markup/inline_parser'
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
class RDoc::Markup::Formatter
|
|
20
16
|
|
|
21
17
|
##
|
|
22
18
|
# Converts a target url to one that is relative to a given path
|
|
@@ -45,21 +41,9 @@ class RDoc::Markup::Formatter
|
|
|
45
41
|
##
|
|
46
42
|
# Creates a new Formatter
|
|
47
43
|
|
|
48
|
-
def initialize
|
|
49
|
-
@
|
|
50
|
-
|
|
51
|
-
@markup = markup || RDoc::Markup.new
|
|
52
|
-
@am = @markup.attribute_manager
|
|
53
|
-
@am.add_regexp_handling(/<br>/, :HARD_BREAK)
|
|
44
|
+
def initialize
|
|
45
|
+
@markup = RDoc::Markup.new
|
|
54
46
|
|
|
55
|
-
@attributes = @am.attributes
|
|
56
|
-
|
|
57
|
-
@attr_tags = []
|
|
58
|
-
|
|
59
|
-
@in_tt = 0
|
|
60
|
-
@tt_bit = @attributes.bitmap_for :TT
|
|
61
|
-
|
|
62
|
-
@hard_break = ''
|
|
63
47
|
@from_path = '.'
|
|
64
48
|
end
|
|
65
49
|
|
|
@@ -84,29 +68,6 @@ class RDoc::Markup::Formatter
|
|
|
84
68
|
@markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
|
|
85
69
|
end
|
|
86
70
|
|
|
87
|
-
##
|
|
88
|
-
# Adds a regexp handling for links of the form {<text>}[<url>] and
|
|
89
|
-
# <word>[<url>]
|
|
90
|
-
|
|
91
|
-
def add_regexp_handling_TIDYLINK
|
|
92
|
-
@markup.add_regexp_handling(/(?:
|
|
93
|
-
\{[^{}]*\} | # multi-word label
|
|
94
|
-
\b[^\s{}]+? # single-word label
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
\[\S+?\] # link target
|
|
98
|
-
/x, :TIDYLINK)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
##
|
|
102
|
-
# Add a new set of tags for an attribute. We allow separate start and end
|
|
103
|
-
# tags for flexibility
|
|
104
|
-
|
|
105
|
-
def add_tag(name, start, stop)
|
|
106
|
-
attr = @attributes.bitmap_for name
|
|
107
|
-
@attr_tags << InlineTag.new(attr, start, stop)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
71
|
##
|
|
111
72
|
# Allows +tag+ to be decorated with additional information.
|
|
112
73
|
|
|
@@ -121,117 +82,169 @@ class RDoc::Markup::Formatter
|
|
|
121
82
|
@markup.convert content, self
|
|
122
83
|
end
|
|
123
84
|
|
|
124
|
-
|
|
125
|
-
# Converts flow items +flow+
|
|
126
|
-
|
|
127
|
-
def convert_flow(flow)
|
|
128
|
-
res = []
|
|
85
|
+
# Applies regexp handling to +text+ and returns an array of [text, converted?] pairs.
|
|
129
86
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
when RDoc::Markup::RegexpHandling then
|
|
138
|
-
res << convert_regexp_handling(item)
|
|
139
|
-
else
|
|
140
|
-
raise "Unknown flow element: #{item.inspect}"
|
|
87
|
+
def apply_regexp_handling(text)
|
|
88
|
+
matched = []
|
|
89
|
+
@markup.regexp_handlings.each_with_index do |(pattern, name), priority|
|
|
90
|
+
text.scan(pattern) do
|
|
91
|
+
m = Regexp.last_match
|
|
92
|
+
idx = m[1] ? 1 : 0
|
|
93
|
+
matched << [m.begin(idx), m.end(idx), m[idx], name, priority]
|
|
141
94
|
end
|
|
142
95
|
end
|
|
96
|
+
# If the start positions are the same, prefer the earlier-registered one
|
|
97
|
+
# (lower numeric priority from each_with_index).
|
|
98
|
+
matched.sort_by! {|beg_pos, _, _, _, priority| [beg_pos, priority] }
|
|
99
|
+
|
|
100
|
+
pos = 0
|
|
101
|
+
output = []
|
|
102
|
+
matched.each do |beg_pos, end_pos, s, name|
|
|
103
|
+
next if beg_pos < pos
|
|
104
|
+
|
|
105
|
+
output << [text[pos...beg_pos], false] if beg_pos != pos
|
|
106
|
+
handled = public_send(:"handle_regexp_#{name}", s)
|
|
107
|
+
output << [handled, true]
|
|
108
|
+
pos = end_pos
|
|
109
|
+
end
|
|
143
110
|
|
|
144
|
-
|
|
111
|
+
output << [text[pos..], false] if pos < text.size
|
|
112
|
+
output
|
|
145
113
|
end
|
|
146
114
|
|
|
147
|
-
|
|
148
|
-
#
|
|
115
|
+
# Called when processing plain text while traversing inline nodes from handle_inline.
|
|
116
|
+
# +text+ may need proper escaping.
|
|
117
|
+
|
|
118
|
+
def handle_PLAIN_TEXT(text)
|
|
119
|
+
end
|
|
149
120
|
|
|
150
|
-
|
|
151
|
-
|
|
121
|
+
# Called when processing regexp-handling-processed text while traversing inline nodes from handle_inline.
|
|
122
|
+
# +text+ may contain markup tags.
|
|
152
123
|
|
|
153
|
-
|
|
124
|
+
def handle_REGEXP_HANDLING_TEXT(text)
|
|
125
|
+
end
|
|
154
126
|
|
|
155
|
-
|
|
156
|
-
|
|
127
|
+
# Called when processing text node while traversing inline nodes from handle_inline.
|
|
128
|
+
# Apply regexp handling and dispatch to the appropriate handler: handle_REGEXP_HANDLING_TEXT or handle_PLAIN_TEXT.
|
|
157
129
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
130
|
+
def handle_TEXT(text)
|
|
131
|
+
apply_regexp_handling(text).each do |part, converted|
|
|
132
|
+
if converted
|
|
133
|
+
handle_REGEXP_HANDLING_TEXT(part)
|
|
134
|
+
else
|
|
135
|
+
handle_PLAIN_TEXT(part)
|
|
161
136
|
end
|
|
162
137
|
end
|
|
138
|
+
end
|
|
163
139
|
|
|
164
|
-
|
|
165
|
-
target_name = @attributes.as_string target.type
|
|
140
|
+
# Called when processing a hard break while traversing inline nodes from handle_inline.
|
|
166
141
|
|
|
167
|
-
|
|
168
|
-
|
|
142
|
+
def handle_HARD_BREAK
|
|
143
|
+
end
|
|
169
144
|
|
|
170
|
-
|
|
145
|
+
# Called when processing bold nodes while traversing inline nodes from handle_inline.
|
|
146
|
+
# Traverse the children nodes and dispatch to the appropriate handlers.
|
|
147
|
+
|
|
148
|
+
def handle_BOLD(nodes)
|
|
149
|
+
traverse_inline_nodes(nodes)
|
|
171
150
|
end
|
|
172
151
|
|
|
173
|
-
|
|
174
|
-
#
|
|
152
|
+
# Called when processing emphasis nodes while traversing inline nodes from handle_inline.
|
|
153
|
+
# Traverse the children nodes and dispatch to the appropriate handlers.
|
|
175
154
|
|
|
176
|
-
def
|
|
177
|
-
|
|
155
|
+
def handle_EM(nodes)
|
|
156
|
+
traverse_inline_nodes(nodes)
|
|
178
157
|
end
|
|
179
158
|
|
|
180
|
-
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
# ##
|
|
184
|
-
# # We don't support raw nodes in ToNoRaw
|
|
185
|
-
#
|
|
186
|
-
# alias accept_raw ignore
|
|
159
|
+
# Called when processing bold word nodes while traversing inline nodes from handle_inline.
|
|
160
|
+
# +word+ may need proper escaping.
|
|
187
161
|
|
|
188
|
-
def
|
|
162
|
+
def handle_BOLD_WORD(word)
|
|
163
|
+
handle_PLAIN_TEXT(word)
|
|
189
164
|
end
|
|
190
165
|
|
|
191
|
-
|
|
192
|
-
#
|
|
166
|
+
# Called when processing emphasis word nodes while traversing inline nodes from handle_inline.
|
|
167
|
+
# +word+ may need proper escaping.
|
|
193
168
|
|
|
194
|
-
def
|
|
195
|
-
|
|
169
|
+
def handle_EM_WORD(word)
|
|
170
|
+
handle_PLAIN_TEXT(word)
|
|
196
171
|
end
|
|
197
172
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
173
|
+
# Called when processing tt nodes while traversing inline nodes from handle_inline.
|
|
174
|
+
# +code+ may need proper escaping.
|
|
175
|
+
|
|
176
|
+
def handle_TT(code)
|
|
177
|
+
handle_PLAIN_TEXT(code)
|
|
203
178
|
end
|
|
204
179
|
|
|
205
|
-
|
|
206
|
-
#
|
|
180
|
+
# Called when processing strike nodes while traversing inline nodes from handle_inline.
|
|
181
|
+
# Traverse the children nodes and dispatch to the appropriate handlers.
|
|
207
182
|
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
res << annotate(tag.on)
|
|
211
|
-
@in_tt += 1 if tt? tag
|
|
212
|
-
end
|
|
183
|
+
def handle_STRIKE(nodes)
|
|
184
|
+
traverse_inline_nodes(nodes)
|
|
213
185
|
end
|
|
214
186
|
|
|
215
|
-
|
|
216
|
-
#
|
|
187
|
+
# Called when processing tidylink nodes while traversing inline nodes from handle_inline.
|
|
188
|
+
# +label_part+ is an array of strings or nodes representing the link label.
|
|
189
|
+
# +url+ is the link URL.
|
|
190
|
+
# Traverse the label_part nodes and dispatch to the appropriate handlers.
|
|
217
191
|
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
@in_tt -= 1 if tt? tag
|
|
221
|
-
res << annotate(tag.off)
|
|
222
|
-
end
|
|
192
|
+
def handle_TIDYLINK(label_part, url)
|
|
193
|
+
traverse_inline_nodes(label_part)
|
|
223
194
|
end
|
|
224
195
|
|
|
225
|
-
|
|
226
|
-
|
|
196
|
+
# Parses inline +text+, traverse the resulting nodes, and calls the appropriate handler methods.
|
|
197
|
+
|
|
198
|
+
def handle_inline(text)
|
|
199
|
+
nodes = RDoc::Markup::InlineParser.new(text).parse
|
|
200
|
+
traverse_inline_nodes(nodes)
|
|
201
|
+
end
|
|
227
202
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
203
|
+
# Traverses +nodes+ and calls the appropriate handler methods
|
|
204
|
+
# Nodes formats are described in RDoc::Markup::InlineParser#parse
|
|
205
|
+
|
|
206
|
+
def traverse_inline_nodes(nodes)
|
|
207
|
+
nodes.each do |node|
|
|
208
|
+
next handle_TEXT(node) if String === node
|
|
209
|
+
case node[:type]
|
|
210
|
+
when :TIDYLINK
|
|
211
|
+
handle_TIDYLINK(node[:children], node[:url])
|
|
212
|
+
when :HARD_BREAK
|
|
213
|
+
handle_HARD_BREAK
|
|
214
|
+
when :BOLD
|
|
215
|
+
handle_BOLD(node[:children])
|
|
216
|
+
when :BOLD_WORD
|
|
217
|
+
handle_BOLD_WORD(node[:children][0] || '')
|
|
218
|
+
when :EM
|
|
219
|
+
handle_EM(node[:children])
|
|
220
|
+
when :EM_WORD
|
|
221
|
+
handle_EM_WORD(node[:children][0] || '')
|
|
222
|
+
when :TT
|
|
223
|
+
handle_TT(node[:children][0] || '')
|
|
224
|
+
when :STRIKE
|
|
225
|
+
handle_STRIKE(node[:children])
|
|
231
226
|
end
|
|
232
227
|
end
|
|
233
228
|
end
|
|
234
229
|
|
|
230
|
+
##
|
|
231
|
+
# Converts a string to be fancier if desired
|
|
232
|
+
|
|
233
|
+
def convert_string(string)
|
|
234
|
+
string
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# Use ignore in your subclass to ignore the content of a node.
|
|
239
|
+
#
|
|
240
|
+
# ##
|
|
241
|
+
# # We don't support raw nodes in ToNoRaw
|
|
242
|
+
#
|
|
243
|
+
# alias accept_raw ignore
|
|
244
|
+
|
|
245
|
+
def ignore(*node)
|
|
246
|
+
end
|
|
247
|
+
|
|
235
248
|
##
|
|
236
249
|
# Extracts and a scheme, url and an anchor id from +url+ and returns them.
|
|
237
250
|
|
data/lib/rdoc/markup/heading.rb
CHANGED
|
@@ -52,13 +52,10 @@ module RDoc
|
|
|
52
52
|
#: () -> RDoc::Markup::ToHtml
|
|
53
53
|
def self.to_html
|
|
54
54
|
@to_html ||= begin
|
|
55
|
-
|
|
56
|
-
markup.add_regexp_handling CrossReference::CROSSREF_REGEXP, :CROSSREF
|
|
55
|
+
to_html = Markup::ToHtml.new
|
|
57
56
|
|
|
58
|
-
to_html
|
|
59
|
-
|
|
60
|
-
def to_html.handle_regexp_CROSSREF(target)
|
|
61
|
-
target.text.sub(/^\\/, '')
|
|
57
|
+
def to_html.handle_regexp_CROSSREF(text)
|
|
58
|
+
text.sub(/^\\/, '')
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
to_html
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'strscan'
|
|
5
|
+
|
|
6
|
+
# Parses inline markup in RDoc text.
|
|
7
|
+
# This parser handles em, bold, strike, tt, hard break, and tidylink.
|
|
8
|
+
# Block-level constructs are handled in RDoc::Markup::Parser.
|
|
9
|
+
|
|
10
|
+
class RDoc::Markup::InlineParser
|
|
11
|
+
|
|
12
|
+
# TT, BOLD_WORD, EM_WORD: regexp-handling(example: crossref) is disabled
|
|
13
|
+
WORD_PAIRS = {
|
|
14
|
+
'*' => :BOLD_WORD,
|
|
15
|
+
'**' => :BOLD_WORD,
|
|
16
|
+
'_' => :EM_WORD,
|
|
17
|
+
'__' => :EM_WORD,
|
|
18
|
+
'+' => :TT,
|
|
19
|
+
'++' => :TT,
|
|
20
|
+
'`' => :TT,
|
|
21
|
+
'``' => :TT
|
|
22
|
+
} # :nodoc:
|
|
23
|
+
|
|
24
|
+
# Other types: regexp-handling(example: crossref) is enabled
|
|
25
|
+
TAGS = {
|
|
26
|
+
'em' => :EM,
|
|
27
|
+
'i' => :EM,
|
|
28
|
+
'b' => :BOLD,
|
|
29
|
+
's' => :STRIKE,
|
|
30
|
+
'del' => :STRIKE,
|
|
31
|
+
} # :nodoc:
|
|
32
|
+
|
|
33
|
+
STANDALONE_TAGS = { 'br' => :HARD_BREAK } # :nodoc:
|
|
34
|
+
|
|
35
|
+
CODEBLOCK_TAGS = %w[tt code] # :nodoc:
|
|
36
|
+
|
|
37
|
+
TOKENS = {
|
|
38
|
+
**WORD_PAIRS.transform_values { [:word_pair, nil] },
|
|
39
|
+
**TAGS.keys.to_h {|tag| ["<#{tag}>", [:open_tag, tag]] },
|
|
40
|
+
**TAGS.keys.to_h {|tag| ["</#{tag}>", [:close_tag, tag]] },
|
|
41
|
+
**CODEBLOCK_TAGS.to_h {|tag| ["<#{tag}>", [:code_start, tag]] },
|
|
42
|
+
**STANDALONE_TAGS.keys.to_h {|tag| ["<#{tag}>", [:standalone_tag, tag]] },
|
|
43
|
+
'{' => [:tidylink_start, nil],
|
|
44
|
+
'}' => [:tidylink_mid, nil],
|
|
45
|
+
'\\' => [:escape, nil],
|
|
46
|
+
'[' => nil # To make `label[url]` scan as separate tokens
|
|
47
|
+
} # :nodoc:
|
|
48
|
+
|
|
49
|
+
multi_char_tokens_regexp = Regexp.union(TOKENS.keys.select {|s| s.size > 1 }).source
|
|
50
|
+
token_starts_regexp = TOKENS.keys.map {|s| s[0] }.uniq.map {|s| Regexp.escape(s) }.join
|
|
51
|
+
|
|
52
|
+
SCANNER_REGEXP =
|
|
53
|
+
/(?:
|
|
54
|
+
#{multi_char_tokens_regexp}
|
|
55
|
+
|[^#{token_starts_regexp}\sa-zA-Z0-9\.]+ # chunk of normal text
|
|
56
|
+
|\s+|[a-zA-Z0-9\.]+|.
|
|
57
|
+
)/x # :nodoc:
|
|
58
|
+
|
|
59
|
+
# Characters that can be escaped with backslash.
|
|
60
|
+
ESCAPING_CHARS = '\\*_+`{}[]<>' # :nodoc:
|
|
61
|
+
|
|
62
|
+
# Pattern to match code block content until <code></tt></code> or <tt></code></tt>.
|
|
63
|
+
CODEBLOCK_REGEXPS = CODEBLOCK_TAGS.to_h {|name| [name, /((?:\\.|[^\\])*?)<\/#{name}>/] } # :nodoc:
|
|
64
|
+
|
|
65
|
+
# Word contains alphanumeric and <tt>_./:[]-</tt> characters.
|
|
66
|
+
# Word may start with <tt>#</tt> and may end with any non-space character. (e.g. <tt>#eql?</tt>).
|
|
67
|
+
# Underscore delimiter have special rules.
|
|
68
|
+
WORD_REGEXPS = {
|
|
69
|
+
# Words including _, longest match.
|
|
70
|
+
# Example: `_::A_` `_-42_` `_A::B::C.foo_bar[baz]_` `_kwarg:_`
|
|
71
|
+
# Content must not include _ followed by non-alphanumeric character
|
|
72
|
+
# Example: `_host_:_port_` will be `_host_` + `:` + `_port_`
|
|
73
|
+
'_' => /#?([a-zA-Z0-9.\/:\[\]-]|_+[a-zA-Z0-9])+[^\s]?_(?=[^a-zA-Z0-9_]|\z)/,
|
|
74
|
+
# Words allowing _ but not allowing __
|
|
75
|
+
'__' => /#?[a-zA-Z0-9.\/:\[\]-]*(_[a-zA-Z0-9.\/:\[\]-]+)*[^\s]?__(?=[^a-zA-Z0-9]|\z)/,
|
|
76
|
+
**%w[* ** + ++ ` ``].to_h do |s|
|
|
77
|
+
# normal words that can be used within +word+ or *word*
|
|
78
|
+
[s, /#?[a-zA-Z0-9_.\/:\[\]-]+[^\s]?#{Regexp.escape(s)}(?=[^a-zA-Z0-9]|\z)/]
|
|
79
|
+
end
|
|
80
|
+
} # :nodoc:
|
|
81
|
+
|
|
82
|
+
def initialize(string)
|
|
83
|
+
@scanner = StringScanner.new(string)
|
|
84
|
+
@last_match = nil
|
|
85
|
+
@scanner_negative_cache = Set.new
|
|
86
|
+
@stack = []
|
|
87
|
+
@delimiters = {}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Return the current parsing node on <tt>@stack</tt>.
|
|
91
|
+
|
|
92
|
+
def current
|
|
93
|
+
@stack.last
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Parse and return an array of nodes.
|
|
97
|
+
# Node format:
|
|
98
|
+
# {
|
|
99
|
+
# type: :EM | :BOLD | :BOLD_WORD | :EM_WORD | :TT | :STRIKE | :HARD_BREAK | :TIDYLINK,
|
|
100
|
+
# url: string # only for :TIDYLINK
|
|
101
|
+
# children: [string_or_node, ...]
|
|
102
|
+
# }
|
|
103
|
+
|
|
104
|
+
def parse
|
|
105
|
+
stack_push(:root, nil)
|
|
106
|
+
while true
|
|
107
|
+
type, token, value = scan_token
|
|
108
|
+
close = nil
|
|
109
|
+
tidylink_url = nil
|
|
110
|
+
case type
|
|
111
|
+
when :node
|
|
112
|
+
current[:children] << value
|
|
113
|
+
invalidate_open_tidylinks if value[:type] == :TIDYLINK
|
|
114
|
+
when :eof
|
|
115
|
+
close = :root
|
|
116
|
+
when :tidylink_open
|
|
117
|
+
stack_push(:tidylink, token)
|
|
118
|
+
when :tidylink_close
|
|
119
|
+
close = :tidylink
|
|
120
|
+
if value
|
|
121
|
+
tidylink_url = value
|
|
122
|
+
else
|
|
123
|
+
# Tidylink closing brace without URL part. Treat opening and closing braces as normal text
|
|
124
|
+
# `{labelnodes}...` case.
|
|
125
|
+
current[:children] << token
|
|
126
|
+
end
|
|
127
|
+
when :invalidated_tidylink_close
|
|
128
|
+
# `{...{label}[url]...}` case. Nested tidylink invalidates outer one. The last `}` closes the invalidated tidylink.
|
|
129
|
+
current[:children] << token
|
|
130
|
+
close = :invalidated_tidylink
|
|
131
|
+
when :text
|
|
132
|
+
current[:children] << token
|
|
133
|
+
when :open
|
|
134
|
+
stack_push(value, token)
|
|
135
|
+
when :close
|
|
136
|
+
if @delimiters[value]
|
|
137
|
+
close = value
|
|
138
|
+
else
|
|
139
|
+
# closing tag without matching opening tag. Treat as normal text.
|
|
140
|
+
current[:children] << token
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
next unless close
|
|
145
|
+
|
|
146
|
+
while current[:delimiter] != close
|
|
147
|
+
children = current[:children]
|
|
148
|
+
open_token = current[:token]
|
|
149
|
+
stack_pop
|
|
150
|
+
current[:children] << open_token if open_token
|
|
151
|
+
current[:children].concat(children)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
token = current[:token]
|
|
155
|
+
children = compact_string(current[:children])
|
|
156
|
+
stack_pop
|
|
157
|
+
|
|
158
|
+
return children if close == :root
|
|
159
|
+
|
|
160
|
+
if close == :tidylink || close == :invalidated_tidylink
|
|
161
|
+
if tidylink_url
|
|
162
|
+
current[:children] << { type: :TIDYLINK, children: children, url: tidylink_url }
|
|
163
|
+
invalidate_open_tidylinks
|
|
164
|
+
else
|
|
165
|
+
current[:children] << token
|
|
166
|
+
current[:children].concat(children)
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
current[:children] << { type: TAGS[close], children: children }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
# When a valid tidylink node is encountered, invalidate all nested tidylinks.
|
|
177
|
+
|
|
178
|
+
def invalidate_open_tidylinks
|
|
179
|
+
return unless @delimiters[:tidylink]
|
|
180
|
+
|
|
181
|
+
@delimiters[:invalidated_tidylink] ||= []
|
|
182
|
+
@delimiters[:tidylink].each do |idx|
|
|
183
|
+
@delimiters[:invalidated_tidylink] << idx
|
|
184
|
+
@stack[idx][:delimiter] = :invalidated_tidylink
|
|
185
|
+
end
|
|
186
|
+
@delimiters.delete(:tidylink)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Pop the top node off the stack when node is closed by a closing delimiter or an error.
|
|
190
|
+
|
|
191
|
+
def stack_pop
|
|
192
|
+
delimiter = current[:delimiter]
|
|
193
|
+
@delimiters[delimiter].pop
|
|
194
|
+
@delimiters.delete(delimiter) if @delimiters[delimiter].empty?
|
|
195
|
+
@stack.pop
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Push a new node onto the stack when encountering an opening delimiter.
|
|
199
|
+
|
|
200
|
+
def stack_push(delimiter, token)
|
|
201
|
+
node = { delimiter: delimiter, token: token, children: [] }
|
|
202
|
+
(@delimiters[delimiter] ||= []) << @stack.size
|
|
203
|
+
@stack << node
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Compacts adjacent strings in +nodes+ into a single string.
|
|
207
|
+
|
|
208
|
+
def compact_string(nodes)
|
|
209
|
+
nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
|
|
210
|
+
is_str ? elems.join : elems
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Scan from StringScanner with +pattern+
|
|
215
|
+
# If +negative_cache+ is true, caches scan failure result. <tt>scan(pattern, negative_cache: true)</tt> return nil when it is called again after a failure.
|
|
216
|
+
# Be careful to use +negative_cache+ with a pattern and position that does not match after previous failure.
|
|
217
|
+
|
|
218
|
+
def strscan(pattern, negative_cache: false)
|
|
219
|
+
return if negative_cache && @scanner_negative_cache.include?(pattern)
|
|
220
|
+
|
|
221
|
+
string = @scanner.scan(pattern)
|
|
222
|
+
@last_match = string if string
|
|
223
|
+
@scanner_negative_cache << pattern if !string && negative_cache
|
|
224
|
+
string
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Scan and return the next token for parsing.
|
|
228
|
+
# Returns <tt>[token_type, token_string_or_nil, extra_info]</tt>
|
|
229
|
+
|
|
230
|
+
def scan_token
|
|
231
|
+
last_match = @last_match
|
|
232
|
+
token = strscan(SCANNER_REGEXP)
|
|
233
|
+
type, name = TOKENS[token]
|
|
234
|
+
|
|
235
|
+
case type
|
|
236
|
+
when :word_pair
|
|
237
|
+
# If the character before word pair delimiter is alphanumeric, do not treat as word pair.
|
|
238
|
+
word_pair = strscan(WORD_REGEXPS[token]) unless /[a-zA-Z0-9]\z/.match?(last_match)
|
|
239
|
+
|
|
240
|
+
if word_pair.nil?
|
|
241
|
+
[:text, token, nil]
|
|
242
|
+
elsif token == '__' && word_pair.match?(/\A[a-zA-Z]+__\z/)
|
|
243
|
+
# Special exception: __FILE__, __LINE__, __send__ should be treated as normal text.
|
|
244
|
+
[:text, "#{token}#{word_pair}", nil]
|
|
245
|
+
else
|
|
246
|
+
[:node, nil, { type: WORD_PAIRS[token], children: [word_pair.delete_suffix(token)] }]
|
|
247
|
+
end
|
|
248
|
+
when :open_tag
|
|
249
|
+
[:open, token, name]
|
|
250
|
+
when :close_tag
|
|
251
|
+
[:close, token, name]
|
|
252
|
+
when :code_start
|
|
253
|
+
if (codeblock = strscan(CODEBLOCK_REGEXPS[name], negative_cache: true))
|
|
254
|
+
# Need to unescape `\\` and `\<`.
|
|
255
|
+
# RDoc also unescapes backslash + word separators, but this is not really necessary.
|
|
256
|
+
content = codeblock.delete_suffix("</#{name}>").gsub(/\\(.)/) { '\\<*+_`'.include?($1) ? $1 : $& }
|
|
257
|
+
[:node, nil, { type: :TT, children: content.empty? ? [] : [content] }]
|
|
258
|
+
else
|
|
259
|
+
[:text, token, nil]
|
|
260
|
+
end
|
|
261
|
+
when :standalone_tag
|
|
262
|
+
[:node, nil, { type: STANDALONE_TAGS[name], children: [] }]
|
|
263
|
+
when :tidylink_start
|
|
264
|
+
[:tidylink_open, token, nil]
|
|
265
|
+
when :tidylink_mid
|
|
266
|
+
if @delimiters[:tidylink]
|
|
267
|
+
if (url = read_tidylink_url)
|
|
268
|
+
[:tidylink_close, nil, url]
|
|
269
|
+
else
|
|
270
|
+
[:tidylink_close, token, nil]
|
|
271
|
+
end
|
|
272
|
+
elsif @delimiters[:invalidated_tidylink]
|
|
273
|
+
[:invalidated_tidylink_close, token, nil]
|
|
274
|
+
else
|
|
275
|
+
[:text, token, nil]
|
|
276
|
+
end
|
|
277
|
+
when :escape
|
|
278
|
+
next_char = strscan(/./)
|
|
279
|
+
if next_char.nil?
|
|
280
|
+
# backslash at end of string
|
|
281
|
+
[:text, '\\', nil]
|
|
282
|
+
elsif next_char && ESCAPING_CHARS.include?(next_char)
|
|
283
|
+
# escaped character
|
|
284
|
+
[:text, next_char, nil]
|
|
285
|
+
else
|
|
286
|
+
# If next_char not an escaping character, it is treated as text token with backslash + next_char
|
|
287
|
+
# For example, backslash of `\Ruby` (suppressed crossref) remains.
|
|
288
|
+
[:text, "\\#{next_char}", nil]
|
|
289
|
+
end
|
|
290
|
+
else
|
|
291
|
+
if token.nil?
|
|
292
|
+
[:eof, nil, nil]
|
|
293
|
+
elsif token.match?(/\A[A-Za-z0-9]*\z/) && (url = read_tidylink_url)
|
|
294
|
+
# Simplified tidylink: label[url]
|
|
295
|
+
[:node, nil, { type: :TIDYLINK, children: [token], url: url }]
|
|
296
|
+
else
|
|
297
|
+
[:text, token, nil]
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Read the URL part of a tidylink from the current position.
|
|
303
|
+
# Returns nil if no valid URL part is found.
|
|
304
|
+
# URL part is enclosed in square brackets and may contain escaped brackets.
|
|
305
|
+
# Example: <tt>[http://example.com/?q=\[\]]</tt> represents <tt>http://example.com/?q=[]</tt>.
|
|
306
|
+
# If we're accepting rdoc-style links in markdown, url may include <tt>*+<_</tt> with backslash escape.
|
|
307
|
+
|
|
308
|
+
def read_tidylink_url
|
|
309
|
+
bracketed_url = strscan(/\[([^\s\[\]\\]|\\[\[\]\\*+<_])+\]/)
|
|
310
|
+
bracketed_url[1...-1].gsub(/\\(.)/, '\1') if bracketed_url
|
|
311
|
+
end
|
|
312
|
+
end
|
data/lib/rdoc/markup/list.rb
CHANGED
|
@@ -37,7 +37,7 @@ class RDoc::Markup::List
|
|
|
37
37
|
# Creates a new list of +type+ with +items+. Valid list types are:
|
|
38
38
|
# +:BULLET+, +:LABEL+, +:LALPHA+, +:NOTE+, +:NUMBER+, +:UALPHA+
|
|
39
39
|
|
|
40
|
-
def initialize
|
|
40
|
+
def initialize(type = nil, *items)
|
|
41
41
|
@type = type
|
|
42
42
|
@items = []
|
|
43
43
|
@items.concat items
|
|
@@ -94,7 +94,7 @@ class RDoc::Markup::List
|
|
|
94
94
|
##
|
|
95
95
|
# Appends +items+ to the list
|
|
96
96
|
|
|
97
|
-
def push
|
|
97
|
+
def push(*items)
|
|
98
98
|
@items.concat items
|
|
99
99
|
end
|
|
100
100
|
|