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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +193 -0
  3. data/LEGAL.rdoc +6 -0
  4. data/LICENSE.rdoc +4 -0
  5. data/README.md +132 -8
  6. data/RI.md +75 -75
  7. data/doc/markup_reference/markdown.md +659 -0
  8. data/doc/markup_reference/rdoc.rdoc +1169 -0
  9. data/exe/rdoc +2 -2
  10. data/lib/rdoc/code_object/alias.rb +70 -74
  11. data/lib/rdoc/code_object/any_method.rb +308 -293
  12. data/lib/rdoc/code_object/attr.rb +150 -142
  13. data/lib/rdoc/code_object/class_module.rb +802 -715
  14. data/lib/rdoc/code_object/constant.rb +181 -144
  15. data/lib/rdoc/code_object/context/section.rb +140 -130
  16. data/lib/rdoc/code_object/context.rb +925 -941
  17. data/lib/rdoc/code_object/extend.rb +7 -5
  18. data/lib/rdoc/code_object/include.rb +7 -5
  19. data/lib/rdoc/code_object/method_attr.rb +328 -315
  20. data/lib/rdoc/code_object/mixin.rb +97 -92
  21. data/lib/rdoc/code_object/normal_class.rb +77 -78
  22. data/lib/rdoc/code_object/normal_module.rb +61 -59
  23. data/lib/rdoc/code_object/require.rb +23 -39
  24. data/lib/rdoc/code_object/single_class.rb +21 -19
  25. data/lib/rdoc/code_object/top_level.rb +215 -202
  26. data/lib/rdoc/code_object.rb +305 -305
  27. data/lib/rdoc/comment.rb +274 -337
  28. data/lib/rdoc/cross_reference.rb +195 -206
  29. data/lib/rdoc/encoding.rb +105 -103
  30. data/lib/rdoc/erb_partial.rb +13 -11
  31. data/lib/rdoc/erbio.rb +29 -27
  32. data/lib/rdoc/generator/aliki.rb +196 -30
  33. data/lib/rdoc/generator/darkfish.rb +647 -629
  34. data/lib/rdoc/generator/json_index.rb +233 -229
  35. data/lib/rdoc/generator/markup.rb +165 -122
  36. data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
  37. data/lib/rdoc/generator/pot/po.rb +52 -51
  38. data/lib/rdoc/generator/pot/po_entry.rb +138 -132
  39. data/lib/rdoc/generator/pot.rb +85 -81
  40. data/lib/rdoc/generator/ri.rb +23 -19
  41. data/lib/rdoc/generator/template/aliki/DESIGN.md +538 -0
  42. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
  43. data/lib/rdoc/generator/template/aliki/_footer.rhtml +2 -2
  44. data/lib/rdoc/generator/template/aliki/_head.rhtml +15 -10
  45. data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
  46. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
  47. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
  48. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  49. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +3 -3
  50. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +3 -3
  51. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  52. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
  53. data/lib/rdoc/generator/template/aliki/class.rhtml +71 -61
  54. data/lib/rdoc/generator/template/aliki/css/rdoc.css +796 -323
  55. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  56. data/lib/rdoc/generator/template/aliki/js/aliki.js +119 -113
  57. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  58. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
  59. data/lib/rdoc/generator/template/aliki/js/{search.js → search_controller.js} +16 -7
  60. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  61. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  62. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  63. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  64. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
  65. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
  66. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
  67. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
  68. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  69. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +2 -2
  70. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  71. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  72. data/lib/rdoc/generator/template/darkfish/class.rhtml +29 -32
  73. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -1
  74. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  75. data/lib/rdoc/generator.rb +48 -46
  76. data/lib/rdoc/i18n/locale.rb +99 -95
  77. data/lib/rdoc/i18n/text.rb +109 -105
  78. data/lib/rdoc/i18n.rb +7 -5
  79. data/lib/rdoc/markdown/byte_runtime.rb +80 -0
  80. data/lib/rdoc/markdown.kpeg +52 -33
  81. data/lib/rdoc/markdown.rb +365 -177
  82. data/lib/rdoc/markup/blank_line.rb +25 -23
  83. data/lib/rdoc/markup/block_quote.rb +12 -8
  84. data/lib/rdoc/markup/document.rb +127 -123
  85. data/lib/rdoc/markup/element.rb +21 -0
  86. data/lib/rdoc/markup/formatter.rb +220 -203
  87. data/lib/rdoc/markup/hard_break.rb +30 -27
  88. data/lib/rdoc/markup/heading.rb +163 -77
  89. data/lib/rdoc/markup/include.rb +33 -29
  90. data/lib/rdoc/markup/indented_paragraph.rb +37 -33
  91. data/lib/rdoc/markup/inline_parser.rb +316 -0
  92. data/lib/rdoc/markup/list.rb +80 -88
  93. data/lib/rdoc/markup/list_item.rb +73 -85
  94. data/lib/rdoc/markup/paragraph.rb +23 -19
  95. data/lib/rdoc/markup/parser.rb +501 -497
  96. data/lib/rdoc/markup/pre_process.rb +284 -305
  97. data/lib/rdoc/markup/raw.rb +52 -55
  98. data/lib/rdoc/markup/rule.rb +16 -12
  99. data/lib/rdoc/markup/table.rb +48 -40
  100. data/lib/rdoc/markup/to_ansi.rb +143 -92
  101. data/lib/rdoc/markup/to_bs.rb +86 -102
  102. data/lib/rdoc/markup/to_html.rb +608 -506
  103. data/lib/rdoc/markup/to_html_crossref.rb +219 -210
  104. data/lib/rdoc/markup/to_html_snippet.rb +210 -205
  105. data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
  106. data/lib/rdoc/markup/to_label.rb +64 -51
  107. data/lib/rdoc/markup/to_markdown.rb +212 -184
  108. data/lib/rdoc/markup/to_rdoc.rb +341 -277
  109. data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
  110. data/lib/rdoc/markup/to_test.rb +62 -50
  111. data/lib/rdoc/markup/to_tt_only.rb +83 -92
  112. data/lib/rdoc/markup/verbatim.rb +62 -58
  113. data/lib/rdoc/markup.rb +196 -215
  114. data/lib/rdoc/options.rb +1063 -1076
  115. data/lib/rdoc/parser/c.rb +1039 -1036
  116. data/lib/rdoc/parser/changelog.rb +320 -287
  117. data/lib/rdoc/parser/markdown.rb +17 -13
  118. data/lib/rdoc/parser/rbs.rb +279 -0
  119. data/lib/rdoc/parser/rd.rb +17 -13
  120. data/lib/rdoc/parser/ruby.rb +1231 -2222
  121. data/lib/rdoc/parser/ruby_colorizer.rb +303 -0
  122. data/lib/rdoc/parser/simple.rb +31 -27
  123. data/lib/rdoc/parser/text.rb +12 -8
  124. data/lib/rdoc/parser.rb +230 -221
  125. data/lib/rdoc/rbs_helper.rb +186 -0
  126. data/lib/rdoc/rd/inline.rb +57 -53
  127. data/lib/rdoc/rd.rb +90 -88
  128. data/lib/rdoc/rdoc.rb +547 -366
  129. data/lib/rdoc/ri/driver.rb +1141 -1130
  130. data/lib/rdoc/ri/formatter.rb +7 -3
  131. data/lib/rdoc/ri/paths.rb +140 -136
  132. data/lib/rdoc/ri/servlet.rb +456 -0
  133. data/lib/rdoc/ri/store.rb +4 -2
  134. data/lib/rdoc/ri/task.rb +55 -51
  135. data/lib/rdoc/ri.rb +14 -11
  136. data/lib/rdoc/rubygems_hook.rb +194 -192
  137. data/lib/rdoc/server.rb +462 -0
  138. data/lib/rdoc/stats/normal.rb +46 -42
  139. data/lib/rdoc/stats/quiet.rb +39 -35
  140. data/lib/rdoc/stats/verbose.rb +35 -31
  141. data/lib/rdoc/stats.rb +363 -338
  142. data/lib/rdoc/store.rb +919 -725
  143. data/lib/rdoc/task.rb +260 -255
  144. data/lib/rdoc/text.rb +164 -240
  145. data/lib/rdoc/token_stream.rb +88 -106
  146. data/lib/rdoc/tom_doc.rb +203 -201
  147. data/lib/rdoc/version.rb +1 -1
  148. data/lib/rdoc.rb +35 -7
  149. data/lib/rubygems_plugin.rb +2 -11
  150. data/rdoc-logo.svg +43 -0
  151. data/rdoc.gemspec +9 -7
  152. metadata +46 -32
  153. data/CONTRIBUTING.rdoc +0 -219
  154. data/ExampleMarkdown.md +0 -39
  155. data/ExampleRDoc.rdoc +0 -210
  156. data/lib/rdoc/code_object/anon_class.rb +0 -10
  157. data/lib/rdoc/code_object/ghost_method.rb +0 -6
  158. data/lib/rdoc/code_object/meta_method.rb +0 -6
  159. data/lib/rdoc/markdown/literals.kpeg +0 -21
  160. data/lib/rdoc/markdown/literals.rb +0 -454
  161. data/lib/rdoc/markup/attr_changer.rb +0 -22
  162. data/lib/rdoc/markup/attr_span.rb +0 -35
  163. data/lib/rdoc/markup/attribute_manager.rb +0 -405
  164. data/lib/rdoc/markup/attributes.rb +0 -70
  165. data/lib/rdoc/markup/regexp_handling.rb +0 -40
  166. data/lib/rdoc/parser/prism_ruby.rb +0 -1100
  167. data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
  168. data/lib/rdoc/parser/ruby_tools.rb +0 -163
  169. data/lib/rdoc/servlet.rb +0 -452
@@ -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="sidebar-navigation" role="navigation" hidden>
7
7
  <%= render '_sidebar_pages.rhtml' %>
8
8
  <%= render '_sidebar_classes.rhtml' %>
9
9
  </nav>
@@ -28,7 +28,7 @@ function createSearchInstance(input, result) {
28
28
 
29
29
  result.classList.remove("initially-hidden");
30
30
 
31
- const search = new Search(search_data, input, result);
31
+ const search = new SearchController(search_data, input, result);
32
32
 
33
33
  search.renderItem = function(result) {
34
34
  const li = document.createElement('li');
@@ -40,8 +40,14 @@ function createSearchInstance(input, result) {
40
40
  html += `<span class="params">${result.params}</span>`;
41
41
  html += '</a>';
42
42
 
43
- if (result.namespace)
44
- html += `<p class="search-namespace">${this.hlt(result.namespace)}`;
43
+ // Add type indicator
44
+ if (result.type) {
45
+ const typeLabel = this.formatType(result.type);
46
+ const typeClass = result.type.replace(/_/g, '-');
47
+ html += `<span class="search-type search-type-${this.escapeHTML(typeClass)}">${typeLabel}</span>`;
48
+ }
49
+
50
+ html += '</p>';
45
51
 
46
52
  if (result.snippet)
47
53
  html += `<div class="search-snippet">${result.snippet}</div>`;
@@ -51,19 +57,20 @@ function createSearchInstance(input, result) {
51
57
  return li;
52
58
  }
53
59
 
54
- search.select = function(result) {
55
- let href = result.firstChild.firstChild.href;
56
- const query = this.input.value;
57
- if (query) {
58
- const url = new URL(href, window.location.origin);
59
- url.searchParams.set('q', query);
60
- url.searchParams.set('nav', '0');
61
- href = url.toString();
62
- }
63
- window.location.href = href;
60
+ search.formatType = function(type) {
61
+ const typeLabels = {
62
+ 'class': 'class',
63
+ 'module': 'module',
64
+ 'constant': 'const',
65
+ 'instance_method': 'method',
66
+ 'class_method': 'method'
67
+ };
68
+ return typeLabels[type] || type;
64
69
  }
65
70
 
66
- search.scrollIntoView = search.scrollInWindow;
71
+ search.select = function(result) {
72
+ window.location.href = result.firstChild.firstChild.href;
73
+ }
67
74
 
68
75
  return search;
69
76
  }
@@ -74,6 +81,11 @@ function hookSearch() {
74
81
 
75
82
  if (!input || !result) return; // Exit if search elements not found
76
83
 
84
+ const container = input.closest('[data-search-container]');
85
+ const searchToggle = document.querySelector('#search-toggle');
86
+ const searchEmpty = container && container.querySelector('.search-empty');
87
+ let focusBeforeOpen = null;
88
+
77
89
  const search_section = document.querySelector('#search-section');
78
90
  if (search_section) {
79
91
  search_section.classList.remove("initially-hidden");
@@ -82,29 +94,92 @@ function hookSearch() {
82
94
  const search = createSearchInstance(input, result);
83
95
  if (!search) return;
84
96
 
97
+ if (searchEmpty) {
98
+ const originalRenderItem = search.renderItem;
99
+ search.renderItem = function(result) {
100
+ searchEmpty.style.display = 'none';
101
+ return originalRenderItem.call(this, result);
102
+ };
103
+ }
104
+
105
+ const showSearchSurface = () => {
106
+ if (container) container.classList.add('is-open');
107
+ if (searchToggle) searchToggle.setAttribute('aria-expanded', 'true');
108
+ };
109
+
110
+ const openSearch = () => {
111
+ focusBeforeOpen = document.activeElement;
112
+ showSearchSurface();
113
+ input.focus({ preventScroll: true });
114
+ };
115
+
116
+ const closeSearch = (restoreFocus = true) => {
117
+ if (container) container.classList.remove('is-open');
118
+ if (searchToggle) searchToggle.setAttribute('aria-expanded', 'false');
119
+ search.hide();
120
+ input.blur();
121
+
122
+ if (restoreFocus && focusBeforeOpen && typeof focusBeforeOpen.focus === 'function') {
123
+ focusBeforeOpen.focus({ preventScroll: true });
124
+ }
125
+ focusBeforeOpen = null;
126
+ };
127
+
128
+ if (searchToggle) searchToggle.addEventListener('click', openSearch);
129
+ if (container) {
130
+ container.querySelectorAll('[data-search-close]').forEach((control) => {
131
+ control.addEventListener('click', () => closeSearch());
132
+ });
133
+ }
134
+
135
+ hookFocus(openSearch);
136
+
137
+ // Hide search results when clicking outside the search area
138
+ document.addEventListener('click', (e) => {
139
+ if (container && container.contains(e.target)) return;
140
+
141
+ search.hide();
142
+ if (container && container.classList.contains('is-open')) closeSearch(false);
143
+ });
144
+
145
+ // Close the search surface on Escape
146
+ document.addEventListener('keydown', (e) => {
147
+ const isOpen = container && container.classList.contains('is-open');
148
+ if (e.key === 'Escape' && (isOpen || input.matches(":focus"))) {
149
+ closeSearch();
150
+ }
151
+ });
152
+
153
+ // Show search results when focusing on input (if there's a query)
154
+ input.addEventListener('focus', () => {
155
+ showSearchSurface();
156
+ if (input.value.trim()) {
157
+ search.show();
158
+ }
159
+ });
160
+
85
161
  // Check for ?q= URL parameter and trigger search automatically
86
162
  if (typeof URLSearchParams !== 'undefined') {
87
163
  const urlParams = new URLSearchParams(window.location.search);
88
164
  const queryParam = urlParams.get('q');
89
165
  if (queryParam) {
90
- const navParam = urlParams.get('nav');
91
- const autoSelect = navParam !== '0';
166
+ openSearch();
92
167
  input.value = queryParam;
93
- search.search(queryParam, autoSelect);
168
+ search.search(queryParam, false);
94
169
  }
95
170
  }
96
171
  }
97
172
 
98
173
  /* ===== Keyboard Shortcuts ===== */
99
174
 
100
- function hookFocus() {
175
+ function hookFocus(openSearch) {
101
176
  document.addEventListener("keydown", (event) => {
102
177
  if (document.activeElement.tagName === 'INPUT') {
103
178
  return;
104
179
  }
105
180
  if (event.key === "/") {
106
181
  event.preventDefault();
107
- document.querySelector('#search-field').focus();
182
+ openSearch();
108
183
  }
109
184
  });
110
185
  }
@@ -112,55 +187,62 @@ function hookFocus() {
112
187
  /* ===== Mobile Navigation ===== */
113
188
 
114
189
  function hookSidebar() {
115
- const navigation = document.querySelector('#navigation');
116
- const navigationToggle = document.querySelector('#navigation-toggle');
190
+ const sidebarNavigation = document.querySelector('#sidebar-navigation');
191
+ const sidebarNavigationToggle = document.querySelector('#sidebar-navigation-toggle');
117
192
 
118
- if (!navigation || !navigationToggle) return;
193
+ if (!sidebarNavigation || !sidebarNavigationToggle) return;
119
194
 
120
195
  const closeNav = () => {
121
- navigation.hidden = true;
122
- navigationToggle.ariaExpanded = 'false';
123
- document.body.classList.remove('nav-open');
196
+ sidebarNavigation.hidden = true;
197
+ sidebarNavigationToggle.ariaExpanded = 'false';
198
+ document.body.classList.remove('sidebar-navigation-open');
124
199
  };
125
200
 
126
201
  const openNav = () => {
127
- navigation.hidden = false;
128
- navigationToggle.ariaExpanded = 'true';
129
- document.body.classList.add('nav-open');
202
+ sidebarNavigation.hidden = false;
203
+ sidebarNavigationToggle.ariaExpanded = 'true';
204
+ document.body.classList.add('sidebar-navigation-open');
130
205
  };
131
206
 
132
207
  const toggleNav = () => {
133
- if (navigation.hidden) {
208
+ if (sidebarNavigation.hidden) {
134
209
  openNav();
135
210
  } else {
136
211
  closeNav();
137
212
  }
138
213
  };
139
214
 
140
- navigationToggle.addEventListener('click', (e) => {
215
+ sidebarNavigationToggle.addEventListener('click', (e) => {
141
216
  e.stopPropagation();
142
217
  toggleNav();
143
218
  });
144
219
 
145
220
  const isSmallViewport = window.matchMedia("(max-width: 1023px)").matches;
221
+
222
+ // The sidebar is hidden by default with the `hidden` attribute
223
+ // On large viewports, we display the sidebar with JavaScript
224
+ // This is better than the opposite approach of hiding it with JavaScript
225
+ // because it avoids flickering the sidebar when the page is loaded, especially on mobile devices
146
226
  if (isSmallViewport) {
147
227
  closeNav();
148
228
 
149
229
  // Close nav when clicking links inside it
150
230
  document.addEventListener('click', (e) => {
151
- if (e.target.closest('#navigation a')) {
231
+ if (e.target.closest('#sidebar-navigation a')) {
152
232
  closeNav();
153
233
  }
154
234
  });
155
235
 
156
236
  // Close nav when clicking backdrop
157
237
  document.addEventListener('click', (e) => {
158
- if (!navigation.hidden &&
159
- !e.target.closest('#navigation') &&
160
- !e.target.closest('#navigation-toggle')) {
238
+ if (!sidebarNavigation.hidden &&
239
+ !e.target.closest('#sidebar-navigation') &&
240
+ !e.target.closest('#sidebar-navigation-toggle')) {
161
241
  closeNav();
162
242
  }
163
243
  });
244
+ } else {
245
+ openNav();
164
246
  }
165
247
  }
166
248
 
@@ -203,7 +285,7 @@ function generateToc() {
203
285
  tocNav.appendChild(tocList);
204
286
  } else {
205
287
  // Hide TOC if no headings found
206
- const tocContainer = document.querySelector('.table-of-contents');
288
+ const tocContainer = document.querySelector('#table-of-contents');
207
289
  if (tocContainer) {
208
290
  tocContainer.style.display = 'none';
209
291
  }
@@ -296,80 +378,6 @@ function setHeadingSelfLinkScrollHandlers() {
296
378
  })
297
379
  }
298
380
 
299
- /* ===== Mobile Search Modal ===== */
300
-
301
- function hookSearchModal() {
302
- const searchToggle = document.querySelector('#search-toggle');
303
- const searchModal = document.querySelector('#search-modal');
304
- const searchModalClose = document.querySelector('#search-modal-close');
305
- const searchModalBackdrop = document.querySelector('.search-modal-backdrop');
306
- const searchInput = document.querySelector('#search-field-mobile');
307
- const searchResults = document.querySelector('#search-results-mobile');
308
- const searchEmpty = document.querySelector('.search-modal-empty');
309
-
310
- if (!searchToggle || !searchModal) return;
311
-
312
- // Initialize search for mobile modal
313
- const mobileSearch = createSearchInstance(searchInput, searchResults);
314
- if (!mobileSearch) return;
315
-
316
- // Hide empty state when there are results
317
- const originalRenderItem = mobileSearch.renderItem;
318
- mobileSearch.renderItem = function(result) {
319
- if (searchEmpty) searchEmpty.style.display = 'none';
320
- return originalRenderItem.call(this, result);
321
- };
322
-
323
- const openSearchModal = () => {
324
- searchModal.hidden = false;
325
- document.body.style.overflow = 'hidden';
326
- // Focus input after animation
327
- setTimeout(() => {
328
- if (searchInput) searchInput.focus();
329
- }, 100);
330
- };
331
-
332
- const closeSearchModal = () => {
333
- searchModal.hidden = true;
334
- document.body.style.overflow = '';
335
- };
336
-
337
- // Open on button click
338
- searchToggle.addEventListener('click', openSearchModal);
339
-
340
- // Close on close button click
341
- if (searchModalClose) {
342
- searchModalClose.addEventListener('click', closeSearchModal);
343
- }
344
-
345
- // Close on backdrop click
346
- if (searchModalBackdrop) {
347
- searchModalBackdrop.addEventListener('click', closeSearchModal);
348
- }
349
-
350
- // Close on Escape key
351
- document.addEventListener('keydown', (e) => {
352
- if (e.key === 'Escape' && !searchModal.hidden) {
353
- closeSearchModal();
354
- }
355
- });
356
-
357
- // Check for ?q= URL parameter on mobile and open modal
358
- if (typeof URLSearchParams !== 'undefined') {
359
- const urlParams = new URLSearchParams(window.location.search);
360
- const queryParam = urlParams.get('q');
361
- const isSmallViewport = window.matchMedia("(max-width: 1023px)").matches;
362
-
363
- if (queryParam && isSmallViewport) {
364
- openSearchModal();
365
- searchInput.value = queryParam;
366
- const navParam = urlParams.get('nav');
367
- const autoSelect = navParam !== '0';
368
- mobileSearch.search(queryParam, autoSelect);
369
- }
370
- }
371
- }
372
-
373
381
  /* ===== Code Block Copy Functionality ===== */
374
382
 
375
383
  function createCopyButton() {
@@ -407,8 +415,8 @@ function wrapCodeBlocksWithCopyButton() {
407
415
  // not directly in rhtml templates
408
416
  // - Modifying the formatter would require extending RDoc's core internals
409
417
 
410
- // Find all pre elements that are not already wrapped
411
- const preElements = document.querySelectorAll('main pre:not(.code-block-wrapper pre)');
418
+ // Target code examples and source code; skip type signature blocks
419
+ const preElements = document.querySelectorAll('main pre:not(.method-type-signature)');
412
420
 
413
421
  preElements.forEach((pre) => {
414
422
  // Skip if already wrapped
@@ -473,11 +481,9 @@ function showCopySuccess(button) {
473
481
  document.addEventListener('DOMContentLoaded', () => {
474
482
  hookSourceViews();
475
483
  hookSearch();
476
- hookFocus();
477
484
  hookSidebar();
478
485
  generateToc();
479
486
  setHeadingSelfLinkScrollHandlers();
480
487
  hookTocActiveHighlighting();
481
- hookSearchModal();
482
488
  wrapCodeBlocksWithCopyButton();
483
489
  });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Client-side shell syntax highlighter for RDoc
3
+ * Highlights: $ prompts, commands, options, strings, env vars, comments
4
+ */
5
+
6
+ (function() {
7
+ 'use strict';
8
+
9
+ function escapeHtml(text) {
10
+ return text
11
+ .replace(/&/g, '&amp;')
12
+ .replace(/</g, '&lt;')
13
+ .replace(/>/g, '&gt;')
14
+ .replace(/"/g, '&quot;')
15
+ .replace(/'/g, '&#39;');
16
+ }
17
+
18
+ function wrap(className, text) {
19
+ return '<span class="' + className + '">' + escapeHtml(text) + '</span>';
20
+ }
21
+
22
+ function highlightLine(line) {
23
+ if (line.trim() === '') return escapeHtml(line);
24
+
25
+ var result = '';
26
+ var i = 0;
27
+ var len = line.length;
28
+
29
+ // Preserve leading whitespace
30
+ while (i < len && (line[i] === ' ' || line[i] === '\t')) {
31
+ result += escapeHtml(line[i++]);
32
+ }
33
+
34
+ // Check for $ prompt ($ followed by space or end of line)
35
+ if (line[i] === '$' && (line[i + 1] === ' ' || line[i + 1] === undefined)) {
36
+ result += wrap('sh-prompt', '$');
37
+ i++;
38
+ }
39
+
40
+ // Check for # comment at start
41
+ if (line[i] === '#') {
42
+ return result + wrap('sh-comment', line.slice(i));
43
+ }
44
+
45
+ var seenCommand = false;
46
+ var afterSpace = true;
47
+
48
+ while (i < len) {
49
+ var ch = line[i];
50
+
51
+ // Whitespace
52
+ if (ch === ' ' || ch === '\t') {
53
+ result += escapeHtml(ch);
54
+ i++;
55
+ afterSpace = true;
56
+ continue;
57
+ }
58
+
59
+ // Comment after whitespace
60
+ if (ch === '#' && afterSpace) {
61
+ result += wrap('sh-comment', line.slice(i));
62
+ break;
63
+ }
64
+
65
+ // Double-quoted string
66
+ if (ch === '"') {
67
+ var end = i + 1;
68
+ while (end < len && line[end] !== '"') {
69
+ if (line[end] === '\\' && end + 1 < len) end += 2;
70
+ else end++;
71
+ }
72
+ if (end < len) end++;
73
+ result += wrap('sh-string', line.slice(i, end));
74
+ i = end;
75
+ afterSpace = false;
76
+ continue;
77
+ }
78
+
79
+ // Single-quoted string
80
+ if (ch === "'") {
81
+ var end = i + 1;
82
+ while (end < len && line[end] !== "'") end++;
83
+ if (end < len) end++;
84
+ result += wrap('sh-string', line.slice(i, end));
85
+ i = end;
86
+ afterSpace = false;
87
+ continue;
88
+ }
89
+
90
+ // Environment variable (ALLCAPS=)
91
+ if (afterSpace && /[A-Z]/.test(ch)) {
92
+ var match = line.slice(i).match(/^[A-Z][A-Z0-9_]*=/);
93
+ if (match) {
94
+ result += wrap('sh-envvar', match[0]);
95
+ i += match[0].length;
96
+ // Read unquoted value
97
+ var valEnd = i;
98
+ while (valEnd < len && line[valEnd] !== ' ' && line[valEnd] !== '\t' && line[valEnd] !== '"' && line[valEnd] !== "'") valEnd++;
99
+ if (valEnd > i) {
100
+ result += escapeHtml(line.slice(i, valEnd));
101
+ i = valEnd;
102
+ }
103
+ afterSpace = false;
104
+ continue;
105
+ }
106
+ }
107
+
108
+ // Option (must be after whitespace)
109
+ if (ch === '-' && afterSpace) {
110
+ var match = line.slice(i).match(/^--?[a-zA-Z0-9_-]+(=[^"'\s]*)?/);
111
+ if (match) {
112
+ result += wrap('sh-option', match[0]);
113
+ i += match[0].length;
114
+ afterSpace = false;
115
+ continue;
116
+ }
117
+ }
118
+
119
+ // Command (first word: regular, ./path, ../path, ~/path, /abs/path, @scope/pkg)
120
+ if (!seenCommand && afterSpace) {
121
+ var isCmd = /[a-zA-Z0-9@~\/]/.test(ch) ||
122
+ (ch === '.' && (line[i + 1] === '/' || (line[i + 1] === '.' && line[i + 2] === '/')));
123
+ if (isCmd) {
124
+ var end = i;
125
+ while (end < len && line[end] !== ' ' && line[end] !== '\t') end++;
126
+ result += wrap('sh-command', line.slice(i, end));
127
+ i = end;
128
+ seenCommand = true;
129
+ afterSpace = false;
130
+ continue;
131
+ }
132
+ }
133
+
134
+ // Everything else
135
+ result += escapeHtml(ch);
136
+ i++;
137
+ afterSpace = false;
138
+ }
139
+
140
+ return result;
141
+ }
142
+
143
+ function highlightShell(code) {
144
+ return code.split('\n').map(highlightLine).join('\n');
145
+ }
146
+
147
+ function initHighlighting() {
148
+ var selectors = [
149
+ 'pre.bash', 'pre.sh', 'pre.shell', 'pre.console',
150
+ 'pre[data-language="bash"]', 'pre[data-language="sh"]',
151
+ 'pre[data-language="shell"]', 'pre[data-language="console"]'
152
+ ];
153
+
154
+ var blocks = document.querySelectorAll(selectors.join(', '));
155
+ blocks.forEach(function(block) {
156
+ if (block.getAttribute('data-highlighted') === 'true') return;
157
+ block.innerHTML = highlightShell(block.textContent);
158
+ block.setAttribute('data-highlighted', 'true');
159
+ });
160
+ }
161
+
162
+ if (document.readyState === 'loading') {
163
+ document.addEventListener('DOMContentLoaded', initHighlighting);
164
+ } else {
165
+ initHighlighting();
166
+ }
167
+ })();
@@ -276,7 +276,7 @@
276
276
  * Initialize C syntax highlighting on page load
277
277
  */
278
278
  function initHighlighting() {
279
- const codeBlocks = document.querySelectorAll('pre[data-language="c"]');
279
+ const codeBlocks = document.querySelectorAll('pre.c');
280
280
 
281
281
  codeBlocks.forEach(block => {
282
282
  if (block.getAttribute('data-highlighted') === 'true') {
@@ -1,15 +1,15 @@
1
- Search = function(data, input, result) {
1
+ SearchController = function(data, input, result) {
2
2
  this.data = data;
3
3
  this.input = input;
4
4
  this.result = result;
5
5
 
6
6
  this.current = null;
7
7
  this.view = this.result.parentNode;
8
- this.searcher = new Searcher(data.index);
8
+ this.ranker = new SearchRanker(data.index);
9
9
  this.init();
10
10
  }
11
11
 
12
- Search.prototype = Object.assign({}, Navigation, new function() {
12
+ SearchController.prototype = Object.assign({}, SearchNavigation, new function() {
13
13
  var suid = 1;
14
14
 
15
15
  this.init = function() {
@@ -25,7 +25,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
25
25
  this.input.addEventListener('keyup', observer);
26
26
  this.input.addEventListener('click', observer); // mac's clear field
27
27
 
28
- this.searcher.ready(function(results, isLast) {
28
+ this.ranker.ready(function(results, isLast) {
29
29
  _this.addResults(results, isLast);
30
30
  })
31
31
 
@@ -36,7 +36,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
36
36
  this.search = function(value, selectFirstMatch) {
37
37
  this.selectFirstMatch = selectFirstMatch;
38
38
 
39
- value = value.trim().toLowerCase();
39
+ value = value.trim();
40
40
  if (value) {
41
41
  this.setNavigationActive(true);
42
42
  } else {
@@ -53,7 +53,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
53
53
  this.result.setAttribute('aria-busy', 'true');
54
54
  this.result.setAttribute('aria-expanded', 'true');
55
55
  this.firstRun = true;
56
- this.searcher.find(value);
56
+ this.ranker.find(value);
57
57
  }
58
58
  }
59
59
 
@@ -96,7 +96,7 @@ Search.prototype = Object.assign({}, Navigation, new function() {
96
96
  this.current.classList.remove('search-selected');
97
97
  next.classList.add('search-selected');
98
98
  this.input.setAttribute('aria-activedescendant', next.getAttribute('id'));
99
- this.scrollIntoView(next, this.view);
99
+ this.scrollInElement(next, this.result);
100
100
  this.current = next;
101
101
  this.input.value = next.firstChild.firstChild.text;
102
102
  this.input.select();
@@ -116,5 +116,14 @@ Search.prototype = Object.assign({}, Navigation, new function() {
116
116
  });
117
117
  }
118
118
 
119
+ this.hide = function() {
120
+ this.result.setAttribute('aria-expanded', 'false');
121
+ this.setNavigationActive(false);
122
+ }
123
+
124
+ this.show = function() {
125
+ this.result.setAttribute('aria-expanded', 'true');
126
+ this.setNavigationActive(true);
127
+ }
119
128
  });
120
129