rdoc 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +1 -3
- data/RI.md +75 -75
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +71 -69
- data/lib/rdoc/code_object/any_method.rb +305 -303
- data/lib/rdoc/code_object/attr.rb +150 -148
- data/lib/rdoc/code_object/class_module.rb +798 -792
- data/lib/rdoc/code_object/constant.rb +175 -173
- data/lib/rdoc/code_object/context/section.rb +142 -138
- data/lib/rdoc/code_object/context.rb +926 -958
- data/lib/rdoc/code_object/extend.rb +7 -5
- data/lib/rdoc/code_object/include.rb +7 -5
- data/lib/rdoc/code_object/method_attr.rb +326 -319
- data/lib/rdoc/code_object/mixin.rb +97 -95
- data/lib/rdoc/code_object/normal_class.rb +77 -78
- data/lib/rdoc/code_object/normal_module.rb +61 -59
- data/lib/rdoc/code_object/require.rb +23 -39
- data/lib/rdoc/code_object/single_class.rb +21 -19
- data/lib/rdoc/code_object/top_level.rb +212 -219
- data/lib/rdoc/code_object.rb +305 -303
- data/lib/rdoc/comment.rb +275 -273
- data/lib/rdoc/cross_reference.rb +192 -190
- data/lib/rdoc/encoding.rb +105 -103
- data/lib/rdoc/erb_partial.rb +13 -11
- data/lib/rdoc/erbio.rb +29 -27
- data/lib/rdoc/generator/aliki.rb +161 -153
- data/lib/rdoc/generator/darkfish.rb +645 -635
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +164 -146
- data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
- data/lib/rdoc/generator/pot/po.rb +52 -51
- data/lib/rdoc/generator/pot/po_entry.rb +138 -132
- data/lib/rdoc/generator/pot.rb +85 -81
- data/lib/rdoc/generator/ri.rb +23 -19
- data/lib/rdoc/generator/template/aliki/DESIGN.md +6 -4
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +10 -10
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +207 -178
- data/lib/rdoc/generator/template/aliki/js/aliki.js +60 -84
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator.rb +48 -46
- data/lib/rdoc/i18n/locale.rb +99 -95
- data/lib/rdoc/i18n/text.rb +109 -105
- data/lib/rdoc/i18n.rb +7 -5
- data/lib/rdoc/markdown/byte_runtime.rb +80 -0
- data/lib/rdoc/markdown.kpeg +15 -11
- data/lib/rdoc/markdown.rb +40 -47
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +219 -215
- data/lib/rdoc/markup/include.rb +33 -29
- data/lib/rdoc/markup/indented_paragraph.rb +37 -33
- data/lib/rdoc/markup/inline_parser.rb +281 -277
- data/lib/rdoc/markup/list.rb +80 -88
- data/lib/rdoc/markup/list_item.rb +73 -85
- data/lib/rdoc/markup/paragraph.rb +23 -19
- data/lib/rdoc/markup/parser.rb +501 -497
- data/lib/rdoc/markup/pre_process.rb +283 -279
- data/lib/rdoc/markup/raw.rb +2 -2
- data/lib/rdoc/markup/rule.rb +16 -12
- data/lib/rdoc/markup/to_ansi.rb +143 -139
- data/lib/rdoc/markup/to_bs.rb +72 -68
- data/lib/rdoc/markup/to_html.rb +594 -565
- data/lib/rdoc/markup/to_html_crossref.rb +234 -230
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +36 -32
- data/lib/rdoc/markup/to_label.rb +63 -59
- data/lib/rdoc/markup/to_markdown.rb +212 -208
- data/lib/rdoc/markup/to_rdoc.rb +336 -332
- data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
- data/lib/rdoc/markup/to_test.rb +60 -56
- data/lib/rdoc/markup/to_tt_only.rb +84 -80
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1061
- data/lib/rdoc/parser/c.rb +1039 -1037
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +239 -235
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1245 -1124
- data/lib/rdoc/parser/ruby_colorizer.rb +263 -213
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +228 -220
- data/lib/rdoc/rbs_helper.rb +1 -1
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +500 -491
- data/lib/rdoc/ri/driver.rb +1140 -1135
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +354 -350
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -12
- data/lib/rdoc/rubygems_hook.rb +183 -181
- data/lib/rdoc/server.rb +349 -347
- data/lib/rdoc/stats/normal.rb +46 -42
- data/lib/rdoc/stats/quiet.rb +39 -35
- data/lib/rdoc/stats/verbose.rb +35 -31
- data/lib/rdoc/stats.rb +365 -363
- data/lib/rdoc/store.rb +888 -902
- data/lib/rdoc/task.rb +260 -256
- data/lib/rdoc/text.rb +135 -133
- data/lib/rdoc/token_stream.rb +101 -93
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- metadata +4 -5
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
|
@@ -3,310 +3,314 @@
|
|
|
3
3
|
require 'set'
|
|
4
4
|
require 'strscan'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
6
|
+
module RDoc
|
|
7
|
+
class Markup
|
|
8
|
+
# Parses inline markup in RDoc text.
|
|
9
|
+
# This parser handles em, bold, strike, tt, hard break, and tidylink.
|
|
10
|
+
# Block-level constructs are handled in RDoc::Markup::Parser.
|
|
11
|
+
|
|
12
|
+
class InlineParser
|
|
13
|
+
|
|
14
|
+
# TT, BOLD_WORD, EM_WORD: regexp-handling(example: crossref) is disabled
|
|
15
|
+
WORD_PAIRS = {
|
|
16
|
+
'*' => :BOLD_WORD,
|
|
17
|
+
'**' => :BOLD_WORD,
|
|
18
|
+
'_' => :EM_WORD,
|
|
19
|
+
'__' => :EM_WORD,
|
|
20
|
+
'+' => :TT,
|
|
21
|
+
'++' => :TT,
|
|
22
|
+
'`' => :TT,
|
|
23
|
+
'``' => :TT
|
|
24
|
+
} # :nodoc:
|
|
25
|
+
|
|
26
|
+
# Other types: regexp-handling(example: crossref) is enabled
|
|
27
|
+
TAGS = {
|
|
28
|
+
'em' => :EM,
|
|
29
|
+
'i' => :EM,
|
|
30
|
+
'b' => :BOLD,
|
|
31
|
+
's' => :STRIKE,
|
|
32
|
+
'del' => :STRIKE,
|
|
33
|
+
} # :nodoc:
|
|
34
|
+
|
|
35
|
+
STANDALONE_TAGS = { 'br' => :HARD_BREAK } # :nodoc:
|
|
36
|
+
|
|
37
|
+
CODEBLOCK_TAGS = %w[tt code] # :nodoc:
|
|
38
|
+
|
|
39
|
+
TOKENS = {
|
|
40
|
+
**WORD_PAIRS.transform_values { [:word_pair, nil] },
|
|
41
|
+
**TAGS.keys.to_h {|tag| ["<#{tag}>", [:open_tag, tag]] },
|
|
42
|
+
**TAGS.keys.to_h {|tag| ["</#{tag}>", [:close_tag, tag]] },
|
|
43
|
+
**CODEBLOCK_TAGS.to_h {|tag| ["<#{tag}>", [:code_start, tag]] },
|
|
44
|
+
**STANDALONE_TAGS.keys.to_h {|tag| ["<#{tag}>", [:standalone_tag, tag]] },
|
|
45
|
+
'{' => [:tidylink_start, nil],
|
|
46
|
+
'}' => [:tidylink_mid, nil],
|
|
47
|
+
'\\' => [:escape, nil],
|
|
48
|
+
'[' => nil # To make `label[url]` scan as separate tokens
|
|
49
|
+
} # :nodoc:
|
|
50
|
+
|
|
51
|
+
multi_char_tokens_regexp = Regexp.union(TOKENS.keys.select {|s| s.size > 1 }).source
|
|
52
|
+
token_starts_regexp = TOKENS.keys.map {|s| s[0] }.uniq.map {|s| Regexp.escape(s) }.join
|
|
53
|
+
|
|
54
|
+
SCANNER_REGEXP =
|
|
55
|
+
/(?:
|
|
56
|
+
#{multi_char_tokens_regexp}
|
|
57
|
+
|[^#{token_starts_regexp}\sa-zA-Z0-9\.]+ # chunk of normal text
|
|
58
|
+
|\s+|[a-zA-Z0-9\.]+|.
|
|
59
|
+
)/x # :nodoc:
|
|
60
|
+
|
|
61
|
+
# Characters that can be escaped with backslash.
|
|
62
|
+
ESCAPING_CHARS = '\\*_+`{}[]<>' # :nodoc:
|
|
63
|
+
|
|
64
|
+
# Pattern to match code block content until <code></tt></code> or <tt></code></tt>.
|
|
65
|
+
CODEBLOCK_REGEXPS = CODEBLOCK_TAGS.to_h {|name| [name, /((?:\\.|[^\\])*?)<\/#{name}>/] } # :nodoc:
|
|
66
|
+
|
|
67
|
+
# Word contains alphanumeric and <tt>_./:[]-</tt> characters.
|
|
68
|
+
# Word may start with <tt>#</tt> and may end with any non-space character. (e.g. <tt>#eql?</tt>).
|
|
69
|
+
# Underscore delimiter have special rules.
|
|
70
|
+
WORD_REGEXPS = {
|
|
71
|
+
# Words including _, longest match.
|
|
72
|
+
# Example: `_::A_` `_-42_` `_A::B::C.foo_bar[baz]_` `_kwarg:_`
|
|
73
|
+
# Content must not include _ followed by non-alphanumeric character
|
|
74
|
+
# Example: `_host_:_port_` will be `_host_` + `:` + `_port_`
|
|
75
|
+
'_' => /#?([a-zA-Z0-9.\/:\[\]-]|_+[a-zA-Z0-9])+[^\s]?_(?=[^a-zA-Z0-9_]|\z)/,
|
|
76
|
+
# Words allowing _ but not allowing __
|
|
77
|
+
'__' => /#?[a-zA-Z0-9.\/:\[\]-]*(_[a-zA-Z0-9.\/:\[\]-]+)*[^\s]?__(?=[^a-zA-Z0-9]|\z)/,
|
|
78
|
+
**%w[* ** + ++ ` ``].to_h do |s|
|
|
79
|
+
# normal words that can be used within +word+ or *word*
|
|
80
|
+
[s, /#?[a-zA-Z0-9_.\/:\[\]-]+[^\s]?#{Regexp.escape(s)}(?=[^a-zA-Z0-9]|\z)/]
|
|
141
81
|
end
|
|
82
|
+
} # :nodoc:
|
|
83
|
+
|
|
84
|
+
def initialize(string)
|
|
85
|
+
@scanner = StringScanner.new(string)
|
|
86
|
+
@last_match = nil
|
|
87
|
+
@scanner_negative_cache = Set.new
|
|
88
|
+
@stack = []
|
|
89
|
+
@delimiters = {}
|
|
142
90
|
end
|
|
143
91
|
|
|
144
|
-
|
|
92
|
+
# Return the current parsing node on <tt>@stack</tt>.
|
|
145
93
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
open_token = current[:token]
|
|
149
|
-
stack_pop
|
|
150
|
-
current[:children] << open_token if open_token
|
|
151
|
-
current[:children].concat(children)
|
|
94
|
+
def current
|
|
95
|
+
@stack.last
|
|
152
96
|
end
|
|
153
97
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
98
|
+
# Parse and return an array of nodes.
|
|
99
|
+
# Node format:
|
|
100
|
+
# {
|
|
101
|
+
# type: :EM | :BOLD | :BOLD_WORD | :EM_WORD | :TT | :STRIKE | :HARD_BREAK | :TIDYLINK,
|
|
102
|
+
# url: string # only for :TIDYLINK
|
|
103
|
+
# children: [string_or_node, ...]
|
|
104
|
+
# }
|
|
105
|
+
|
|
106
|
+
def parse
|
|
107
|
+
stack_push(:root, nil)
|
|
108
|
+
while true
|
|
109
|
+
type, token, value = scan_token
|
|
110
|
+
close = nil
|
|
111
|
+
tidylink_url = nil
|
|
112
|
+
case type
|
|
113
|
+
when :node
|
|
114
|
+
current[:children] << value
|
|
115
|
+
invalidate_open_tidylinks if value[:type] == :TIDYLINK
|
|
116
|
+
when :eof
|
|
117
|
+
close = :root
|
|
118
|
+
when :tidylink_open
|
|
119
|
+
stack_push(:tidylink, token)
|
|
120
|
+
when :tidylink_close
|
|
121
|
+
close = :tidylink
|
|
122
|
+
if value
|
|
123
|
+
tidylink_url = value
|
|
124
|
+
else
|
|
125
|
+
# Tidylink closing brace without URL part. Treat opening and closing braces as normal text
|
|
126
|
+
# `{labelnodes}...` case.
|
|
127
|
+
current[:children] << token
|
|
128
|
+
end
|
|
129
|
+
when :invalidated_tidylink_close
|
|
130
|
+
# `{...{label}[url]...}` case. Nested tidylink invalidates outer one. The last `}` closes the invalidated tidylink.
|
|
131
|
+
current[:children] << token
|
|
132
|
+
close = :invalidated_tidylink
|
|
133
|
+
when :text
|
|
134
|
+
current[:children] << token
|
|
135
|
+
when :open
|
|
136
|
+
stack_push(value, token)
|
|
137
|
+
when :close
|
|
138
|
+
if @delimiters[value]
|
|
139
|
+
close = value
|
|
140
|
+
else
|
|
141
|
+
# closing tag without matching opening tag. Treat as normal text.
|
|
142
|
+
current[:children] << token
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
next unless close
|
|
147
|
+
|
|
148
|
+
while current[:delimiter] != close
|
|
149
|
+
children = current[:children]
|
|
150
|
+
open_token = current[:token]
|
|
151
|
+
stack_pop
|
|
152
|
+
current[:children] << open_token if open_token
|
|
153
|
+
current[:children].concat(children)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
token = current[:token]
|
|
157
|
+
children = compact_string(current[:children])
|
|
158
|
+
stack_pop
|
|
159
|
+
|
|
160
|
+
return children if close == :root
|
|
161
|
+
|
|
162
|
+
if close == :tidylink || close == :invalidated_tidylink
|
|
163
|
+
if tidylink_url
|
|
164
|
+
current[:children] << { type: :TIDYLINK, children: children, url: tidylink_url }
|
|
165
|
+
invalidate_open_tidylinks
|
|
166
|
+
else
|
|
167
|
+
current[:children] << token
|
|
168
|
+
current[:children].concat(children)
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
current[:children] << { type: TAGS[close], children: children }
|
|
172
|
+
end
|
|
167
173
|
end
|
|
168
|
-
else
|
|
169
|
-
current[:children] << { type: TAGS[close], children: children }
|
|
170
174
|
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
175
|
|
|
174
176
|
private
|
|
175
177
|
|
|
176
|
-
|
|
178
|
+
# When a valid tidylink node is encountered, invalidate all nested tidylinks.
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
def invalidate_open_tidylinks
|
|
181
|
+
return unless @delimiters[:tidylink]
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# Pop the top node off the stack when node is closed by a closing delimiter or an error.
|
|
183
|
+
@delimiters[:invalidated_tidylink] ||= []
|
|
184
|
+
@delimiters[:tidylink].each do |idx|
|
|
185
|
+
@delimiters[:invalidated_tidylink] << idx
|
|
186
|
+
@stack[idx][:delimiter] = :invalidated_tidylink
|
|
187
|
+
end
|
|
188
|
+
@delimiters.delete(:tidylink)
|
|
189
|
+
end
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
delimiter = current[:delimiter]
|
|
193
|
-
@delimiters[delimiter].pop
|
|
194
|
-
@delimiters.delete(delimiter) if @delimiters[delimiter].empty?
|
|
195
|
-
@stack.pop
|
|
196
|
-
end
|
|
191
|
+
# Pop the top node off the stack when node is closed by a closing delimiter or an error.
|
|
197
192
|
|
|
198
|
-
|
|
193
|
+
def stack_pop
|
|
194
|
+
delimiter = current[:delimiter]
|
|
195
|
+
@delimiters[delimiter].pop
|
|
196
|
+
@delimiters.delete(delimiter) if @delimiters[delimiter].empty?
|
|
197
|
+
@stack.pop
|
|
198
|
+
end
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
node = { delimiter: delimiter, token: token, children: [] }
|
|
202
|
-
(@delimiters[delimiter] ||= []) << @stack.size
|
|
203
|
-
@stack << node
|
|
204
|
-
end
|
|
200
|
+
# Push a new node onto the stack when encountering an opening delimiter.
|
|
205
201
|
|
|
206
|
-
|
|
202
|
+
def stack_push(delimiter, token)
|
|
203
|
+
node = { delimiter: delimiter, token: token, children: [] }
|
|
204
|
+
(@delimiters[delimiter] ||= []) << @stack.size
|
|
205
|
+
@stack << node
|
|
206
|
+
end
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
|
|
210
|
-
is_str ? elems.join : elems
|
|
211
|
-
end
|
|
212
|
-
end
|
|
208
|
+
# Compacts adjacent strings in +nodes+ into a single string.
|
|
213
209
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
210
|
+
def compact_string(nodes)
|
|
211
|
+
nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
|
|
212
|
+
is_str ? elems.join : elems
|
|
213
|
+
end
|
|
214
|
+
end
|
|
217
215
|
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
# Scan from StringScanner with +pattern+
|
|
217
|
+
# 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.
|
|
218
|
+
# Be careful to use +negative_cache+ with a pattern and position that does not match after previous failure.
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
@scanner_negative_cache << pattern if !string && negative_cache
|
|
224
|
-
string
|
|
225
|
-
end
|
|
220
|
+
def strscan(pattern, negative_cache: false)
|
|
221
|
+
return if negative_cache && @scanner_negative_cache.include?(pattern)
|
|
226
222
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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]
|
|
223
|
+
string = @scanner.scan(pattern)
|
|
224
|
+
@last_match = string if string
|
|
225
|
+
@scanner_negative_cache << pattern if !string && negative_cache
|
|
226
|
+
string
|
|
260
227
|
end
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
228
|
+
|
|
229
|
+
# Scan and return the next token for parsing.
|
|
230
|
+
# Returns <tt>[token_type, token_string_or_nil, extra_info]</tt>
|
|
231
|
+
|
|
232
|
+
def scan_token
|
|
233
|
+
last_match = @last_match
|
|
234
|
+
token = strscan(SCANNER_REGEXP)
|
|
235
|
+
type, name = TOKENS[token]
|
|
236
|
+
|
|
237
|
+
case type
|
|
238
|
+
when :word_pair
|
|
239
|
+
# If the character before word pair delimiter is alphanumeric, do not treat as word pair.
|
|
240
|
+
word_pair = strscan(WORD_REGEXPS[token]) unless /[a-zA-Z0-9]\z/.match?(last_match)
|
|
241
|
+
|
|
242
|
+
if word_pair.nil?
|
|
243
|
+
[:text, token, nil]
|
|
244
|
+
elsif token == '__' && word_pair.match?(/\A[a-zA-Z]+__\z/)
|
|
245
|
+
# Special exception: __FILE__, __LINE__, __send__ should be treated as normal text.
|
|
246
|
+
[:text, "#{token}#{word_pair}", nil]
|
|
247
|
+
else
|
|
248
|
+
[:node, nil, { type: WORD_PAIRS[token], children: [word_pair.delete_suffix(token)] }]
|
|
249
|
+
end
|
|
250
|
+
when :open_tag
|
|
251
|
+
[:open, token, name]
|
|
252
|
+
when :close_tag
|
|
253
|
+
[:close, token, name]
|
|
254
|
+
when :code_start
|
|
255
|
+
if (codeblock = strscan(CODEBLOCK_REGEXPS[name], negative_cache: true))
|
|
256
|
+
# Need to unescape `\\` and `\<`.
|
|
257
|
+
# RDoc also unescapes backslash + word separators, but this is not really necessary.
|
|
258
|
+
content = codeblock.delete_suffix("</#{name}>").gsub(/\\(.)/) { '\\<*+_`'.include?($1) ? $1 : $& }
|
|
259
|
+
[:node, nil, { type: :TT, children: content.empty? ? [] : [content] }]
|
|
260
|
+
else
|
|
261
|
+
[:text, token, nil]
|
|
262
|
+
end
|
|
263
|
+
when :standalone_tag
|
|
264
|
+
[:node, nil, { type: STANDALONE_TAGS[name], children: [] }]
|
|
265
|
+
when :tidylink_start
|
|
266
|
+
[:tidylink_open, token, nil]
|
|
267
|
+
when :tidylink_mid
|
|
268
|
+
if @delimiters[:tidylink]
|
|
269
|
+
if (url = read_tidylink_url)
|
|
270
|
+
[:tidylink_close, nil, url]
|
|
271
|
+
else
|
|
272
|
+
[:tidylink_close, token, nil]
|
|
273
|
+
end
|
|
274
|
+
elsif @delimiters[:invalidated_tidylink]
|
|
275
|
+
[:invalidated_tidylink_close, token, nil]
|
|
276
|
+
else
|
|
277
|
+
[:text, token, nil]
|
|
278
|
+
end
|
|
279
|
+
when :escape
|
|
280
|
+
next_char = strscan(/./)
|
|
281
|
+
if next_char.nil?
|
|
282
|
+
# backslash at end of string
|
|
283
|
+
[:text, '\\', nil]
|
|
284
|
+
elsif next_char && ESCAPING_CHARS.include?(next_char)
|
|
285
|
+
# escaped character
|
|
286
|
+
[:text, next_char, nil]
|
|
287
|
+
else
|
|
288
|
+
# If next_char not an escaping character, it is treated as text token with backslash + next_char
|
|
289
|
+
# For example, backslash of `\Ruby` (suppressed crossref) remains.
|
|
290
|
+
[:text, "\\#{next_char}", nil]
|
|
291
|
+
end
|
|
269
292
|
else
|
|
270
|
-
|
|
293
|
+
if token.nil?
|
|
294
|
+
[:eof, nil, nil]
|
|
295
|
+
elsif token.match?(/\A[A-Za-z0-9]*\z/) && (url = read_tidylink_url)
|
|
296
|
+
# Simplified tidylink: label[url]
|
|
297
|
+
[:node, nil, { type: :TIDYLINK, children: [token], url: url }]
|
|
298
|
+
else
|
|
299
|
+
[:text, token, nil]
|
|
300
|
+
end
|
|
271
301
|
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
302
|
end
|
|
299
|
-
end
|
|
300
|
-
end
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
# Read the URL part of a tidylink from the current position.
|
|
305
|
+
# Returns nil if no valid URL part is found.
|
|
306
|
+
# URL part is enclosed in square brackets and may contain escaped brackets.
|
|
307
|
+
# Example: <tt>[http://example.com/?q=\[\]]</tt> represents <tt>http://example.com/?q=[]</tt>.
|
|
308
|
+
# If we're accepting rdoc-style links in markdown, url may include <tt>*+<_</tt> with backslash escape.
|
|
307
309
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
def read_tidylink_url
|
|
311
|
+
bracketed_url = strscan(/\[([^\s\[\]\\]|\\[\[\]\\*+<_])+\]/)
|
|
312
|
+
bracketed_url[1...-1].gsub(/\\(.)/, '\1') if bracketed_url
|
|
313
|
+
end
|
|
314
|
+
end
|
|
311
315
|
end
|
|
312
316
|
end
|