rdoc 6.17.0 → 7.2.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +196 -0
  3. data/LEGAL.rdoc +6 -0
  4. data/README.md +90 -7
  5. data/doc/markup_reference/markdown.md +558 -0
  6. data/doc/markup_reference/rdoc.rdoc +1169 -0
  7. data/lib/rdoc/code_object/any_method.rb +15 -7
  8. data/lib/rdoc/code_object/attr.rb +2 -1
  9. data/lib/rdoc/code_object/class_module.rb +62 -11
  10. data/lib/rdoc/code_object/constant.rb +9 -0
  11. data/lib/rdoc/code_object/context/section.rb +46 -9
  12. data/lib/rdoc/code_object/context.rb +15 -4
  13. data/lib/rdoc/code_object/method_attr.rb +13 -1
  14. data/lib/rdoc/code_object/mixin.rb +3 -0
  15. data/lib/rdoc/code_object/top_level.rb +15 -1
  16. data/lib/rdoc/comment.rb +1 -1
  17. data/lib/rdoc/cross_reference.rb +31 -24
  18. data/lib/rdoc/generator/aliki.rb +141 -0
  19. data/lib/rdoc/generator/darkfish.rb +3 -1
  20. data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
  21. data/lib/rdoc/generator/template/aliki/_head.rhtml +9 -4
  22. data/lib/rdoc/generator/template/aliki/_header.rhtml +4 -4
  23. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +1 -1
  24. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +3 -3
  25. data/lib/rdoc/generator/template/aliki/class.rhtml +17 -17
  26. data/lib/rdoc/generator/template/aliki/css/rdoc.css +278 -60
  27. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  28. data/lib/rdoc/generator/template/aliki/js/aliki.js +51 -23
  29. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  30. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
  31. data/lib/rdoc/generator/template/aliki/js/{search.js → search_controller.js} +16 -7
  32. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  33. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  34. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  35. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  36. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +1 -1
  37. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
  38. data/lib/rdoc/generator/template/darkfish/class.rhtml +11 -11
  39. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -0
  40. data/lib/rdoc/markdown.kpeg +22 -12
  41. data/lib/rdoc/markdown.rb +36 -26
  42. data/lib/rdoc/markup/blank_line.rb +25 -23
  43. data/lib/rdoc/markup/element.rb +21 -0
  44. data/lib/rdoc/markup/formatter.rb +129 -106
  45. data/lib/rdoc/markup/hard_break.rb +30 -27
  46. data/lib/rdoc/markup/heading.rb +166 -77
  47. data/lib/rdoc/markup/inline_parser.rb +312 -0
  48. data/lib/rdoc/markup/parser.rb +1 -1
  49. data/lib/rdoc/markup/raw.rb +52 -55
  50. data/lib/rdoc/markup/table.rb +48 -40
  51. data/lib/rdoc/markup/to_ansi.rb +51 -4
  52. data/lib/rdoc/markup/to_bs.rb +22 -42
  53. data/lib/rdoc/markup/to_html.rb +178 -183
  54. data/lib/rdoc/markup/to_html_crossref.rb +58 -79
  55. data/lib/rdoc/markup/to_html_snippet.rb +62 -62
  56. data/lib/rdoc/markup/to_label.rb +29 -20
  57. data/lib/rdoc/markup/to_markdown.rb +61 -37
  58. data/lib/rdoc/markup/to_rdoc.rb +86 -26
  59. data/lib/rdoc/markup/to_test.rb +9 -1
  60. data/lib/rdoc/markup/to_tt_only.rb +10 -16
  61. data/lib/rdoc/markup/verbatim.rb +1 -1
  62. data/lib/rdoc/markup.rb +11 -32
  63. data/lib/rdoc/options.rb +1 -1
  64. data/lib/rdoc/parser/changelog.rb +29 -0
  65. data/lib/rdoc/parser/prism_ruby.rb +44 -32
  66. data/lib/rdoc/parser/ruby.rb +1 -1
  67. data/lib/rdoc/rubygems_hook.rb +3 -3
  68. data/lib/rdoc/text.rb +44 -5
  69. data/lib/rdoc/token_stream.rb +4 -8
  70. data/lib/rdoc/version.rb +1 -1
  71. data/rdoc.gemspec +3 -3
  72. metadata +13 -15
  73. data/CONTRIBUTING.rdoc +0 -219
  74. data/ExampleMarkdown.md +0 -39
  75. data/ExampleRDoc.rdoc +0 -210
  76. data/lib/rdoc/markup/attr_changer.rb +0 -22
  77. data/lib/rdoc/markup/attr_span.rb +0 -35
  78. data/lib/rdoc/markup/attribute_manager.rb +0 -405
  79. data/lib/rdoc/markup/attributes.rb +0 -70
  80. data/lib/rdoc/markup/regexp_handling.rb +0 -40
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Aliki Search Implementation
3
+ *
4
+ * Search algorithm with the following priorities:
5
+ * 1. Exact full_name match always wins (for namespace/method queries)
6
+ * 2. Exact name match gets high priority
7
+ * 3. Match types:
8
+ * - Namespace queries (::) and method queries (# or .) match against full_name
9
+ * - Regular queries match against unqualified name
10
+ * - Prefix (10000) > substring (5000) > fuzzy (1000)
11
+ * 4. First character determines type priority:
12
+ * - Starts with lowercase: methods first
13
+ * - Starts with uppercase: classes/modules/constants first
14
+ * 5. Within same type priority:
15
+ * - Unqualified match > qualified match
16
+ * - Shorter name > longer name
17
+ * 6. Class methods > instance methods
18
+ * 7. Result limit: 30
19
+ * 8. Minimum query length: 1 character
20
+ */
21
+
22
+ var MAX_RESULTS = 30;
23
+ var MIN_QUERY_LENGTH = 1;
24
+
25
+ /*
26
+ * Scoring constants - organized in tiers where each tier dominates lower tiers.
27
+ * This ensures match type always beats type priority, etc.
28
+ *
29
+ * Tier 0: Exact matches (immediate return)
30
+ * Tier 1: Match type (prefix > substring > fuzzy)
31
+ * Tier 2: Exact name bonus
32
+ * Tier 3: Type priority (method vs class based on query case)
33
+ * Tier 4: Minor bonuses (top-level, class method, name length)
34
+ */
35
+ var SCORE_EXACT_FULL_NAME = 1000000; // Tier 0: Query exactly matches full_name
36
+ var SCORE_MATCH_PREFIX = 10000; // Tier 1: Query is prefix of name
37
+ var SCORE_MATCH_SUBSTRING = 5000; // Tier 1: Query is substring of name
38
+ var SCORE_MATCH_FUZZY = 1000; // Tier 1: Query chars appear in order
39
+ var SCORE_EXACT_NAME = 500; // Tier 2: Name exactly equals query
40
+ var SCORE_TYPE_PRIORITY = 100; // Tier 3: Preferred type (method/class)
41
+ var SCORE_TOP_LEVEL = 50; // Tier 4: Top-level over namespaced
42
+ var SCORE_CLASS_METHOD = 10; // Tier 4: Class method over instance method
43
+
44
+ /**
45
+ * Check if all characters in query appear in order in target
46
+ * e.g., "addalias" fuzzy matches "add_foo_alias"
47
+ */
48
+ function fuzzyMatch(target, query) {
49
+ var ti = 0;
50
+ for (var qi = 0; qi < query.length; qi++) {
51
+ ti = target.indexOf(query[qi], ti);
52
+ if (ti === -1) return false;
53
+ ti++;
54
+ }
55
+ return true;
56
+ }
57
+
58
+ /**
59
+ * Parse and normalize a search query
60
+ * @param {string} query - The raw search query
61
+ * @returns {Object} Parsed query with normalized form and flags
62
+ */
63
+ function parseQuery(query) {
64
+ // Lowercase for case-insensitive matching (so "hash" finds both Hash class and #hash methods)
65
+ var normalized = query.toLowerCase();
66
+ var isNamespaceQuery = query.includes('::');
67
+ var isMethodQuery = query.includes('#') || query.includes('.');
68
+
69
+ // Normalize . to :: (RDoc uses :: for class methods in full_name)
70
+ if (query.includes('.')) {
71
+ normalized = normalized.replace(/\./g, '::');
72
+ }
73
+
74
+ return {
75
+ original: query,
76
+ normalized: normalized,
77
+ isNamespaceQuery: isNamespaceQuery,
78
+ isMethodQuery: isMethodQuery,
79
+ // Namespace and method queries match against full_name instead of name
80
+ matchesFullName: isNamespaceQuery || isMethodQuery,
81
+ // If query starts with lowercase, prioritize methods; otherwise prioritize classes/modules/constants
82
+ prioritizeMethod: !/^[A-Z]/.test(query)
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Main search function
88
+ * @param {string} query - The search query
89
+ * @param {Array} index - The search index to search in
90
+ * @returns {Array} Array of matching entries, sorted by relevance
91
+ */
92
+ function search(query, index) {
93
+ if (!query || query.length < MIN_QUERY_LENGTH) {
94
+ return [];
95
+ }
96
+
97
+ var q = parseQuery(query);
98
+ var results = [];
99
+
100
+ for (var i = 0; i < index.length; i++) {
101
+ var entry = index[i];
102
+ var score = computeScore(entry, q);
103
+
104
+ if (score !== null) {
105
+ results.push({ entry: entry, score: score });
106
+ }
107
+ }
108
+
109
+ results.sort(function(a, b) {
110
+ return b.score - a.score;
111
+ });
112
+
113
+ return results.slice(0, MAX_RESULTS).map(function(r) {
114
+ return r.entry;
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Compute the relevance score for an entry
120
+ * @param {Object} entry - The search index entry
121
+ * @param {Object} q - Parsed query from parseQuery()
122
+ * @returns {number|null} Score or null if no match
123
+ */
124
+ function computeScore(entry, q) {
125
+ var name = entry.name;
126
+ var fullName = entry.full_name;
127
+ var type = entry.type;
128
+
129
+ var nameLower = name.toLowerCase();
130
+ var fullNameLower = fullName.toLowerCase();
131
+
132
+ // Exact full_name match (e.g., "Array#filter" matches Array#filter)
133
+ if (q.matchesFullName && fullNameLower === q.normalized) {
134
+ return SCORE_EXACT_FULL_NAME;
135
+ }
136
+
137
+ var matchScore = 0;
138
+ var target = q.matchesFullName ? fullNameLower : nameLower;
139
+
140
+ if (target.startsWith(q.normalized)) {
141
+ matchScore = SCORE_MATCH_PREFIX; // Prefix (e.g., "Arr" matches "Array")
142
+ } else if (target.includes(q.normalized)) {
143
+ matchScore = SCORE_MATCH_SUBSTRING; // Substring (e.g., "ray" matches "Array")
144
+ } else if (fuzzyMatch(target, q.normalized)) {
145
+ matchScore = SCORE_MATCH_FUZZY; // Fuzzy (e.g., "addalias" matches "add_foo_alias")
146
+ } else {
147
+ return null;
148
+ }
149
+
150
+ var score = matchScore;
151
+ var isMethod = (type === 'instance_method' || type === 'class_method');
152
+
153
+ if (q.prioritizeMethod ? isMethod : !isMethod) {
154
+ score += SCORE_TYPE_PRIORITY;
155
+ }
156
+
157
+ if (type === 'class_method') score += SCORE_CLASS_METHOD;
158
+ if (name === fullName) score += SCORE_TOP_LEVEL; // Top-level (Hash) > namespaced (Foo::Hash)
159
+ if (nameLower === q.normalized) score += SCORE_EXACT_NAME; // Exact name match
160
+ score -= name.length;
161
+
162
+ return score;
163
+ }
164
+
165
+ /**
166
+ * SearchRanker class for compatibility with the Search UI
167
+ * Provides ready() and find() interface
168
+ */
169
+ function SearchRanker(index) {
170
+ this.index = index;
171
+ this.handlers = [];
172
+ }
173
+
174
+ SearchRanker.prototype.ready = function(fn) {
175
+ this.handlers.push(fn);
176
+ };
177
+
178
+ SearchRanker.prototype.find = function(query) {
179
+ var q = parseQuery(query);
180
+ var rawResults = search(query, this.index);
181
+ var results = rawResults.map(function(entry) {
182
+ return formatResult(entry, q);
183
+ });
184
+
185
+ var _this = this;
186
+ this.handlers.forEach(function(fn) {
187
+ fn.call(_this, results, true);
188
+ });
189
+ };
190
+
191
+ /**
192
+ * Format a search result entry for display
193
+ */
194
+ function formatResult(entry, q) {
195
+ var result = {
196
+ title: highlightMatch(entry.full_name, q),
197
+ path: entry.path,
198
+ type: entry.type
199
+ };
200
+
201
+ if (entry.snippet) {
202
+ result.snippet = entry.snippet;
203
+ }
204
+
205
+ return result;
206
+ }
207
+
208
+ /**
209
+ * Add highlight markers (\u0001 and \u0002) to matching portions of text
210
+ * @param {string} text - The text to highlight
211
+ * @param {Object} q - Parsed query from parseQuery()
212
+ */
213
+ function highlightMatch(text, q) {
214
+ if (!text || !q) return text;
215
+
216
+ var textLower = text.toLowerCase();
217
+ var query = q.normalized;
218
+
219
+ // Try contiguous match first (prefix or substring)
220
+ var matchIndex = textLower.indexOf(query);
221
+ if (matchIndex !== -1) {
222
+ return text.substring(0, matchIndex) +
223
+ '\u0001' + text.substring(matchIndex, matchIndex + query.length) + '\u0002' +
224
+ text.substring(matchIndex + query.length);
225
+ }
226
+
227
+ // Fall back to fuzzy highlight (highlight each matched character)
228
+ var result = '';
229
+ var ti = 0;
230
+ for (var qi = 0; qi < query.length; qi++) {
231
+ var charIndex = textLower.indexOf(query[qi], ti);
232
+ if (charIndex === -1) return text;
233
+ result += text.substring(ti, charIndex);
234
+ result += '\u0001' + text[charIndex] + '\u0002';
235
+ ti = charIndex + 1;
236
+ }
237
+ result += text.substring(ti);
238
+ return result;
239
+ }
@@ -3,7 +3,7 @@
3
3
  <%= render '_header.rhtml' %>
4
4
  <%= render '_sidebar_toggle.rhtml' %>
5
5
 
6
- <nav id="navigation" role="navigation">
6
+ <nav id="navigation" role="navigation" hidden>
7
7
  <%= render '_sidebar_pages.rhtml' %>
8
8
  <%= render '_sidebar_classes.rhtml' %>
9
9
  </nav>
@@ -1,7 +1,7 @@
1
1
  <body role="document">
2
2
  <%= render '_sidebar_toggle.rhtml' %>
3
3
 
4
- <nav id="navigation" role="navigation">
4
+ <nav id="navigation" role="navigation" hidden>
5
5
  <%= render '_sidebar_pages.rhtml' %>
6
6
  <%= render '_sidebar_classes.rhtml' %>
7
7
  </nav>
@@ -1,7 +1,7 @@
1
1
  <body role="document">
2
2
  <%= render '_sidebar_toggle.rhtml' %>
3
3
 
4
- <nav id="navigation" role="navigation">
4
+ <nav id="navigation" role="navigation" hidden>
5
5
  <div id="project-navigation">
6
6
  <div id="home-section" class="nav-section">
7
7
  <h2>
@@ -13,7 +13,7 @@
13
13
  <%- f = files.shift %>
14
14
  <%- if files.empty? %>
15
15
  <li><a href="<%= rel_prefix %>/<%= h f.path %>"><%= h f.page_name %></a></li>
16
- <%- next %>
16
+ <%- next -%>
17
17
  <%- end %>
18
18
  <li><details<%= ' open' if dir == n %>><summary><%
19
19
  if n == f.page_name
@@ -33,6 +33,7 @@
33
33
  </ol>
34
34
  <% end %>
35
35
 
36
+ <span id="<%= h klass.legacy_aref %>" class="legacy-anchor"></span>
36
37
  <h1 id="<%= h klass.aref %>" class="anchor-link <%= klass.type %>">
37
38
  <%= klass.type %> <%= klass.full_name %>
38
39
  </h1>
@@ -42,6 +43,7 @@
42
43
  </section>
43
44
 
44
45
  <%- klass.each_section do |section, constants, attributes| %>
46
+ <span id="<%= section.legacy_aref %>" class="legacy-anchor"></span>
45
47
  <section id="<%= section.aref %>" class="documentation-section anchor-link">
46
48
  <%- if section.title then %>
47
49
  <header class="documentation-section-title">
@@ -117,10 +119,10 @@
117
119
  </section>
118
120
  <%- end %>
119
121
 
120
- <%- klass.methods_by_type(section).each do |type, visibilities|
121
- next if visibilities.empty?
122
- visibilities.each do |visibility, methods|
123
- next if methods.empty? %>
122
+ <%- klass.methods_by_type(section).each do |type, visibilities| %>
123
+ <%- next if visibilities.empty? %>
124
+ <%- visibilities.each do |visibility, methods| %>
125
+ <%- next if methods.empty? %>
124
126
  <section id="<%= visibility %>-<%= type %>-<%= section.aref %>-method-details" class="method-section anchor-link">
125
127
  <header>
126
128
  <h3><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
@@ -163,15 +165,13 @@
163
165
  <summary>Source</summary>
164
166
  </details>
165
167
  </div>
168
+ <div class="method-source-code" id="<%= method.html_name %>-source">
169
+ <pre><%= method.markup_code %></pre>
170
+ </div>
166
171
  <%- end %>
167
172
 
168
173
  <%- unless method.skip_description? then %>
169
174
  <div class="method-description">
170
- <%- if method.token_stream then %>
171
- <div class="method-source-code" id="<%= method.html_name %>-source">
172
- <pre><%= method.markup_code %></pre>
173
- </div>
174
- <%- end %>
175
175
  <%- if method.mixin_from then %>
176
176
  <div class="mixin-from">
177
177
  <%= method.singleton ? "Extended" : "Included" %> from <a href="<%= klass.aref_to(method.mixin_from.path) %>"><%= method.mixin_from.full_name %></a>
@@ -215,8 +215,8 @@
215
215
 
216
216
  <%- end %>
217
217
  </section>
218
- <%- end
219
- end %>
218
+ <%- end %>
219
+ <%- end %>
220
220
  </section>
221
221
  <%- end %>
222
222
  </main>
@@ -92,6 +92,25 @@ main .anchor-link:target {
92
92
  scroll-margin-top: 1rem;
93
93
  }
94
94
 
95
+ /* Legacy anchor for backward compatibility with old label- prefix links */
96
+ .legacy-anchor {
97
+ display: block;
98
+ position: relative;
99
+ visibility: hidden;
100
+ scroll-margin-top: 1rem;
101
+ }
102
+
103
+ /* When a legacy anchor is targeted, highlight the next heading sibling */
104
+ .legacy-anchor:target + h1,
105
+ .legacy-anchor:target + h2,
106
+ .legacy-anchor:target + h3,
107
+ .legacy-anchor:target + h4,
108
+ .legacy-anchor:target + h5,
109
+ .legacy-anchor:target + h6 {
110
+ margin-left: -10px;
111
+ border-left: 10px solid var(--border-color);
112
+ }
113
+
95
114
  /* 4. Links */
96
115
  a {
97
116
  color: var(--link-color);
@@ -303,6 +303,20 @@
303
303
  end
304
304
  end
305
305
 
306
+ # Escape character that has special meaning in RDoc format.
307
+ # To allow rdoc-styled link used in markdown format for now, bracket and brace are not escaped.
308
+
309
+ def rdoc_escape(text)
310
+ text.gsub(/[*+<\\_]/) {|s| "\\#{s}" }
311
+ end
312
+
313
+ # Escape link url that contains brackets.
314
+ # Brackets needs escape because link url will be surrounded by `[]` in RDoc format.
315
+
316
+ def rdoc_link_url_escape(text)
317
+ text.gsub(/[\[\]\\]/) {|s| "\\#{s}" }
318
+ end
319
+
306
320
  ##
307
321
  # :category: Extensions
308
322
  #
@@ -490,11 +504,7 @@
490
504
  # Wraps `text` in strike markup for rdoc inline formatting
491
505
 
492
506
  def strike text
493
- if text =~ /\A[a-z\d.\/-]+\z/i then
494
- "~#{text}~"
495
- else
496
- "<s>#{text}</s>"
497
- end
507
+ "<del>#{text}</del>"
498
508
  end
499
509
 
500
510
  ##
@@ -973,11 +983,11 @@ Space = @Spacechar+ { " " }
973
983
 
974
984
  Str = @StartList:a
975
985
  < @NormalChar+ > { a = text }
976
- ( StrChunk:c { a << c } )* { a }
986
+ ( StrChunk:c { a << c } )* { rdoc_escape(a) }
977
987
 
978
988
  StrChunk = < (@NormalChar | /_+/ &Alphanumeric)+ > { text }
979
989
 
980
- EscapedChar = "\\" !@Newline < /[:\\`|*_{}\[\]()#+.!><-]/ > { text }
990
+ EscapedChar = "\\" !@Newline < /[:\\`|*_{}\[\]()#+.!><-]/ > { rdoc_escape(text) }
981
991
 
982
992
  Entity = ( HexEntity | DecEntity | CharEntity ):a { a }
983
993
 
@@ -992,7 +1002,7 @@ TerminalEndline = @Sp @Newline @Eof
992
1002
  LineBreak = " " @NormalEndline { RDoc::Markup::HardBreak.new }
993
1003
 
994
1004
  Symbol = < @SpecialChar >
995
- { text }
1005
+ { rdoc_escape(text) }
996
1006
 
997
1007
  # This keeps the parser from getting bogged down on long strings of '*' or '_',
998
1008
  # or strings of '*' or '_' with space on each side:
@@ -1057,7 +1067,7 @@ ReferenceLinkSingle = Label:content < (Spnl "[]")? >
1057
1067
  { link_to content, content, text }
1058
1068
 
1059
1069
  ExplicitLink = ExplicitLinkWithLabel:a
1060
- { "{#{a[:label]}}[#{a[:link]}]" }
1070
+ { "{#{a[:label]}}[#{rdoc_link_url_escape(a[:link])}]" }
1061
1071
 
1062
1072
  ExplicitLinkWithLabel = Label:label "(" @Sp Source:link Spnl Title @Sp ")"
1063
1073
  { { label: label, link: link } }
@@ -1167,12 +1177,12 @@ Newline = %literals.Newline
1167
1177
  Spacechar = %literals.Spacechar
1168
1178
 
1169
1179
  HexEntity = /&#x/i < /[0-9a-fA-F]+/ > ";"
1170
- { [text.to_i(16)].pack 'U' }
1180
+ { rdoc_escape([text.to_i(16)].pack('U')) }
1171
1181
  DecEntity = "&#" < /[0-9]+/ > ";"
1172
- { [text.to_i].pack 'U' }
1182
+ { rdoc_escape([text.to_i].pack('U')) }
1173
1183
  CharEntity = "&" </[A-Za-z0-9]+/ > ";"
1174
1184
  { if entity = HTML_ENTITIES[text] then
1175
- entity.pack 'U*'
1185
+ rdoc_escape(entity.pack('U*'))
1176
1186
  else
1177
1187
  "&#{text};"
1178
1188
  end
data/lib/rdoc/markdown.rb CHANGED
@@ -688,6 +688,20 @@ class RDoc::Markdown
688
688
  end
689
689
  end
690
690
 
691
+ # Escape character that has special meaning in RDoc format.
692
+ # To allow rdoc-styled link used in markdown format for now, bracket and brace are not escaped.
693
+
694
+ def rdoc_escape(text)
695
+ text.gsub(/[*+<\\_]/) {|s| "\\#{s}" }
696
+ end
697
+
698
+ # Escape link url that contains brackets.
699
+ # Brackets needs escape because link url will be surrounded by `[]` in RDoc format.
700
+
701
+ def rdoc_link_url_escape(text)
702
+ text.gsub(/[\[\]\\]/) {|s| "\\#{s}" }
703
+ end
704
+
691
705
  ##
692
706
  # :category: Extensions
693
707
  #
@@ -875,11 +889,7 @@ class RDoc::Markdown
875
889
  # Wraps `text` in strike markup for rdoc inline formatting
876
890
 
877
891
  def strike text
878
- if text =~ /\A[a-z\d.\/-]+\z/i then
879
- "~#{text}~"
880
- else
881
- "<s>#{text}</s>"
882
- end
892
+ "<del>#{text}</del>"
883
893
  end
884
894
 
885
895
  ##
@@ -9735,7 +9745,7 @@ class RDoc::Markdown
9735
9745
  return _tmp
9736
9746
  end
9737
9747
 
9738
- # Str = @StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }
9748
+ # Str = @StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { rdoc_escape(a) }
9739
9749
  def _Str
9740
9750
 
9741
9751
  _save = self.pos
@@ -9796,7 +9806,7 @@ class RDoc::Markdown
9796
9806
  self.pos = _save
9797
9807
  break
9798
9808
  end
9799
- @result = begin; a ; end
9809
+ @result = begin; rdoc_escape(a) ; end
9800
9810
  _tmp = true
9801
9811
  unless _tmp
9802
9812
  self.pos = _save
@@ -9898,7 +9908,7 @@ class RDoc::Markdown
9898
9908
  return _tmp
9899
9909
  end
9900
9910
 
9901
- # EscapedChar = "\\" !@Newline < /[:\\`|*_{}\[\]()#+.!><-]/ > { text }
9911
+ # EscapedChar = "\\" !@Newline < /[:\\`|*_{}\[\]()#+.!><-]/ > { rdoc_escape(text) }
9902
9912
  def _EscapedChar
9903
9913
 
9904
9914
  _save = self.pos
@@ -9925,7 +9935,7 @@ class RDoc::Markdown
9925
9935
  self.pos = _save
9926
9936
  break
9927
9937
  end
9928
- @result = begin; text ; end
9938
+ @result = begin; rdoc_escape(text) ; end
9929
9939
  _tmp = true
9930
9940
  unless _tmp
9931
9941
  self.pos = _save
@@ -10126,7 +10136,7 @@ class RDoc::Markdown
10126
10136
  return _tmp
10127
10137
  end
10128
10138
 
10129
- # Symbol = < @SpecialChar > { text }
10139
+ # Symbol = < @SpecialChar > { rdoc_escape(text) }
10130
10140
  def _Symbol
10131
10141
 
10132
10142
  _save = self.pos
@@ -10140,7 +10150,7 @@ class RDoc::Markdown
10140
10150
  self.pos = _save
10141
10151
  break
10142
10152
  end
10143
- @result = begin; text ; end
10153
+ @result = begin; rdoc_escape(text) ; end
10144
10154
  _tmp = true
10145
10155
  unless _tmp
10146
10156
  self.pos = _save
@@ -11193,7 +11203,7 @@ class RDoc::Markdown
11193
11203
  return _tmp
11194
11204
  end
11195
11205
 
11196
- # ExplicitLink = ExplicitLinkWithLabel:a { "{#{a[:label]}}[#{a[:link]}]" }
11206
+ # ExplicitLink = ExplicitLinkWithLabel:a { "{#{a[:label]}}[#{rdoc_link_url_escape(a[:link])}]" }
11197
11207
  def _ExplicitLink
11198
11208
 
11199
11209
  _save = self.pos
@@ -11204,7 +11214,7 @@ class RDoc::Markdown
11204
11214
  self.pos = _save
11205
11215
  break
11206
11216
  end
11207
- @result = begin; "{#{a[:label]}}[#{a[:link]}]" ; end
11217
+ @result = begin; "{#{a[:label]}}[#{rdoc_link_url_escape(a[:link])}]" ; end
11208
11218
  _tmp = true
11209
11219
  unless _tmp
11210
11220
  self.pos = _save
@@ -14619,7 +14629,7 @@ class RDoc::Markdown
14619
14629
  return _tmp
14620
14630
  end
14621
14631
 
14622
- # HexEntity = /&#x/i < /[0-9a-fA-F]+/ > ";" { [text.to_i(16)].pack 'U' }
14632
+ # HexEntity = /&#x/i < /[0-9a-fA-F]+/ > ";" { rdoc_escape([text.to_i(16)].pack('U')) }
14623
14633
  def _HexEntity
14624
14634
 
14625
14635
  _save = self.pos
@@ -14643,7 +14653,7 @@ class RDoc::Markdown
14643
14653
  self.pos = _save
14644
14654
  break
14645
14655
  end
14646
- @result = begin; [text.to_i(16)].pack 'U' ; end
14656
+ @result = begin; rdoc_escape([text.to_i(16)].pack('U')) ; end
14647
14657
  _tmp = true
14648
14658
  unless _tmp
14649
14659
  self.pos = _save
@@ -14655,7 +14665,7 @@ class RDoc::Markdown
14655
14665
  return _tmp
14656
14666
  end
14657
14667
 
14658
- # DecEntity = "&#" < /[0-9]+/ > ";" { [text.to_i].pack 'U' }
14668
+ # DecEntity = "&#" < /[0-9]+/ > ";" { rdoc_escape([text.to_i].pack('U')) }
14659
14669
  def _DecEntity
14660
14670
 
14661
14671
  _save = self.pos
@@ -14679,7 +14689,7 @@ class RDoc::Markdown
14679
14689
  self.pos = _save
14680
14690
  break
14681
14691
  end
14682
- @result = begin; [text.to_i].pack 'U' ; end
14692
+ @result = begin; rdoc_escape([text.to_i].pack('U')) ; end
14683
14693
  _tmp = true
14684
14694
  unless _tmp
14685
14695
  self.pos = _save
@@ -14691,7 +14701,7 @@ class RDoc::Markdown
14691
14701
  return _tmp
14692
14702
  end
14693
14703
 
14694
- # CharEntity = "&" < /[A-Za-z0-9]+/ > ";" { if entity = HTML_ENTITIES[text] then entity.pack 'U*' else "&#{text};" end }
14704
+ # CharEntity = "&" < /[A-Za-z0-9]+/ > ";" { if entity = HTML_ENTITIES[text] then rdoc_escape(entity.pack('U*')) else "&#{text};" end }
14695
14705
  def _CharEntity
14696
14706
 
14697
14707
  _save = self.pos
@@ -14716,7 +14726,7 @@ class RDoc::Markdown
14716
14726
  break
14717
14727
  end
14718
14728
  @result = begin; if entity = HTML_ENTITIES[text] then
14719
- entity.pack 'U*'
14729
+ rdoc_escape(entity.pack('U*'))
14720
14730
  else
14721
14731
  "&#{text};"
14722
14732
  end
@@ -16567,15 +16577,15 @@ class RDoc::Markdown
16567
16577
  Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c !(&{ github? } Ticks3 /[^`\\n]*$/) &Inline { c })+:chunks @Endline? { chunks }")
16568
16578
  Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)")
16569
16579
  Rules[:_Space] = rule_info("Space", "@Spacechar+ { \" \" }")
16570
- Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }")
16580
+ Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { rdoc_escape(a) }")
16571
16581
  Rules[:_StrChunk] = rule_info("StrChunk", "< (@NormalChar | /_+/ &Alphanumeric)+ > { text }")
16572
- Rules[:_EscapedChar] = rule_info("EscapedChar", "\"\\\\\" !@Newline < /[:\\\\`|*_{}\\[\\]()\#+.!><-]/ > { text }")
16582
+ Rules[:_EscapedChar] = rule_info("EscapedChar", "\"\\\\\" !@Newline < /[:\\\\`|*_{}\\[\\]()\#+.!><-]/ > { rdoc_escape(text) }")
16573
16583
  Rules[:_Entity] = rule_info("Entity", "(HexEntity | DecEntity | CharEntity):a { a }")
16574
16584
  Rules[:_Endline] = rule_info("Endline", "(@LineBreak | @TerminalEndline | @NormalEndline)")
16575
16585
  Rules[:_NormalEndline] = rule_info("NormalEndline", "@Sp @Newline !@BlankLine !\">\" !AtxStart !(Line /={1,}|-{1,}/ @Newline) { \"\\n\" }")
16576
16586
  Rules[:_TerminalEndline] = rule_info("TerminalEndline", "@Sp @Newline @Eof")
16577
16587
  Rules[:_LineBreak] = rule_info("LineBreak", "\" \" @NormalEndline { RDoc::Markup::HardBreak.new }")
16578
- Rules[:_Symbol] = rule_info("Symbol", "< @SpecialChar > { text }")
16588
+ Rules[:_Symbol] = rule_info("Symbol", "< @SpecialChar > { rdoc_escape(text) }")
16579
16589
  Rules[:_UlOrStarLine] = rule_info("UlOrStarLine", "(UlLine | StarLine):a { a }")
16580
16590
  Rules[:_StarLine] = rule_info("StarLine", "(< /\\*{4,}/ > { text } | < @Spacechar /\\*+/ &@Spacechar > { text })")
16581
16591
  Rules[:_UlLine] = rule_info("UlLine", "(< /_{4,}/ > { text } | < @Spacechar /_+/ &@Spacechar > { text })")
@@ -16592,7 +16602,7 @@ class RDoc::Markdown
16592
16602
  Rules[:_ReferenceLink] = rule_info("ReferenceLink", "(ReferenceLinkDouble | ReferenceLinkSingle)")
16593
16603
  Rules[:_ReferenceLinkDouble] = rule_info("ReferenceLinkDouble", "Label:content < Spnl > !\"[]\" Label:label { link_to content, label, text }")
16594
16604
  Rules[:_ReferenceLinkSingle] = rule_info("ReferenceLinkSingle", "Label:content < (Spnl \"[]\")? > { link_to content, content, text }")
16595
- Rules[:_ExplicitLink] = rule_info("ExplicitLink", "ExplicitLinkWithLabel:a { \"{\#{a[:label]}}[\#{a[:link]}]\" }")
16605
+ Rules[:_ExplicitLink] = rule_info("ExplicitLink", "ExplicitLinkWithLabel:a { \"{\#{a[:label]}}[\#{rdoc_link_url_escape(a[:link])}]\" }")
16596
16606
  Rules[:_ExplicitLinkWithLabel] = rule_info("ExplicitLinkWithLabel", "Label:label \"(\" @Sp Source:link Spnl Title @Sp \")\" { { label: label, link: link } }")
16597
16607
  Rules[:_Source] = rule_info("Source", "(\"<\" < SourceContents > \">\" | < SourceContents >) { text }")
16598
16608
  Rules[:_SourceContents] = rule_info("SourceContents", "((!\"(\" !\")\" !\">\" Nonspacechar)+ | \"(\" SourceContents \")\")*")
@@ -16635,9 +16645,9 @@ class RDoc::Markdown
16635
16645
  Rules[:_BOM] = rule_info("BOM", "%literals.BOM")
16636
16646
  Rules[:_Newline] = rule_info("Newline", "%literals.Newline")
16637
16647
  Rules[:_Spacechar] = rule_info("Spacechar", "%literals.Spacechar")
16638
- Rules[:_HexEntity] = rule_info("HexEntity", "/&\#x/i < /[0-9a-fA-F]+/ > \";\" { [text.to_i(16)].pack 'U' }")
16639
- Rules[:_DecEntity] = rule_info("DecEntity", "\"&\#\" < /[0-9]+/ > \";\" { [text.to_i].pack 'U' }")
16640
- Rules[:_CharEntity] = rule_info("CharEntity", "\"&\" < /[A-Za-z0-9]+/ > \";\" { if entity = HTML_ENTITIES[text] then entity.pack 'U*' else \"&\#{text};\" end }")
16648
+ Rules[:_HexEntity] = rule_info("HexEntity", "/&\#x/i < /[0-9a-fA-F]+/ > \";\" { rdoc_escape([text.to_i(16)].pack('U')) }")
16649
+ Rules[:_DecEntity] = rule_info("DecEntity", "\"&\#\" < /[0-9]+/ > \";\" { rdoc_escape([text.to_i].pack('U')) }")
16650
+ Rules[:_CharEntity] = rule_info("CharEntity", "\"&\" < /[A-Za-z0-9]+/ > \";\" { if entity = HTML_ENTITIES[text] then rdoc_escape(entity.pack('U*')) else \"&\#{text};\" end }")
16641
16651
  Rules[:_NonindentSpace] = rule_info("NonindentSpace", "/ {0,3}/")
16642
16652
  Rules[:_Indent] = rule_info("Indent", "/\\t| /")
16643
16653
  Rules[:_IndentedLine] = rule_info("IndentedLine", "Indent Line")