apex-ruby 1.0.18 → 1.0.20

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 (207) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -3
  3. data/ext/apex_ext/apex_ext.c +260 -91
  4. data/ext/apex_ext/apex_src/CHANGELOG.md +440 -0
  5. data/ext/apex_ext/apex_src/CMakeLists.txt +15 -1
  6. data/ext/apex_ext/apex_src/Formula/apex.rb +7 -2
  7. data/ext/apex_ext/apex_src/Package.swift +15 -4
  8. data/ext/apex_ext/apex_src/README.md +94 -35
  9. data/ext/apex_ext/apex_src/VERSION +1 -1
  10. data/ext/apex_ext/apex_src/apex-debug.log +4 -0
  11. data/ext/apex_ext/apex_src/apex.code-workspace +38 -0
  12. data/ext/apex_ext/apex_src/apex.taskpaper +12 -0
  13. data/ext/apex_ext/apex_src/apex_deflist_debug.log +25 -0
  14. data/ext/apex_ext/apex_src/buildnotes.md +1435 -0
  15. data/ext/apex_ext/apex_src/cli/main.c +262 -33
  16. data/ext/apex_ext/apex_src/debug.log +7 -0
  17. data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +274 -0
  18. data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +749 -0
  19. data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +744 -0
  20. data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +382 -0
  21. data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +351 -0
  22. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +745 -0
  23. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +829 -0
  24. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +1148 -0
  25. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +161 -0
  26. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +184 -0
  27. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +211 -0
  28. data/ext/apex_ext/apex_src/documentation/app-settings-table.md +67 -0
  29. data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +340 -0
  30. data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +11 -0
  31. data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +234 -0
  32. data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +516 -0
  33. data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +461 -0
  34. data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +193 -0
  35. data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +201 -0
  36. data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +678 -0
  37. data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +1367 -0
  38. data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +6 -0
  39. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +774 -300
  40. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +1194 -0
  41. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +416 -216
  42. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3544 -1094
  43. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +371 -97
  44. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +153 -23
  45. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +358 -124
  46. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +1656 -0
  47. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +1226 -0
  48. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +201 -56
  49. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +279 -109
  50. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +200 -53
  51. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +2327 -0
  52. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +690 -271
  53. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +201 -55
  54. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +238 -68
  55. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +421 -221
  56. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +340 -78
  57. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +251 -86
  58. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +1362 -0
  59. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +277 -90
  60. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +673 -346
  61. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +1405 -0
  62. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +1902 -0
  63. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +1939 -1698
  64. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +1781 -0
  65. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +315 -140
  66. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +307 -113
  67. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +1132 -0
  68. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +1754 -0
  69. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +872 -376
  70. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
  71. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
  72. data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +8746 -0
  73. data/ext/apex_ext/apex_src/documentation/shared_styles.css +20 -15
  74. data/ext/apex_ext/apex_src/documentation/transform_for_app.md +55 -0
  75. data/ext/apex_ext/apex_src/escaping-repro.md +103 -0
  76. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +8 -0
  77. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/QuickLook/Preview.png +0 -0
  78. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/document.wflow +547 -0
  79. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown-to-RTF.workflow.zip +0 -0
  80. data/ext/apex_ext/apex_src/examples/quick-actions/README.md +33 -0
  81. data/ext/apex_ext/apex_src/include/apex/apex.h +92 -6
  82. data/ext/apex_ext/apex_src/include/apex/ast_rtf.h +23 -0
  83. data/ext/apex_ext/apex_src/include/apex/ast_terminal.h +6 -0
  84. data/ext/apex_ext/apex_src/include/apex/module.modulemap +5 -0
  85. data/ext/apex_ext/apex_src/include/apex/plugins.h +105 -0
  86. data/ext/apex_ext/apex_src/include/apexc.h +1 -0
  87. data/ext/apex_ext/apex_src/man/apex.1 +82 -78
  88. data/ext/apex_ext/apex_src/man/apex.1.md +81 -4
  89. data/ext/apex_ext/apex_src/objc/Apex.swift +73 -0
  90. data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +79 -0
  91. data/ext/apex_ext/apex_src/objc/NSString+Apex.h +83 -5
  92. data/ext/apex_ext/apex_src/objc/NSString+Apex.m +244 -1
  93. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +76 -0
  94. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +169 -0
  95. data/ext/apex_ext/apex_src/output.html +87 -0
  96. data/ext/apex_ext/apex_src/plans/quarto-mode.md +442 -0
  97. data/ext/apex_ext/apex_src/src/_README.md +85 -32
  98. data/ext/apex_ext/apex_src/src/apex.c +770 -200
  99. data/ext/apex_ext/apex_src/src/ast_rtf.c +848 -0
  100. data/ext/apex_ext/apex_src/src/ast_terminal.c +184 -7
  101. data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +10 -3
  102. data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +7 -11
  103. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.c +467 -0
  104. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.h +27 -0
  105. data/ext/apex_ext/apex_src/src/extensions/callouts.c +400 -4
  106. data/ext/apex_ext/apex_src/src/extensions/callouts.h +15 -5
  107. data/ext/apex_ext/apex_src/src/extensions/citations.c +26 -5
  108. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.c +632 -0
  109. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.h +22 -0
  110. data/ext/apex_ext/apex_src/src/extensions/concordance.c +632 -0
  111. data/ext/apex_ext/apex_src/src/extensions/definition_list.c +129 -15
  112. data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1050 -121
  113. data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +569 -16
  114. data/ext/apex_ext/apex_src/src/extensions/ial.c +502 -52
  115. data/ext/apex_ext/apex_src/src/extensions/ial.h +2 -1
  116. data/ext/apex_ext/apex_src/src/extensions/includes.c +532 -33
  117. data/ext/apex_ext/apex_src/src/extensions/includes.h +7 -1
  118. data/ext/apex_ext/apex_src/src/extensions/index.c +1427 -140
  119. data/ext/apex_ext/apex_src/src/extensions/index.h +28 -0
  120. data/ext/apex_ext/apex_src/src/extensions/metadata.c +200 -161
  121. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.c +251 -0
  122. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.h +22 -0
  123. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.c +624 -0
  124. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.h +35 -0
  125. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.c +264 -0
  126. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.h +23 -0
  127. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.c +307 -0
  128. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.h +21 -0
  129. data/ext/apex_ext/apex_src/src/extensions/raw_content.c +351 -0
  130. data/ext/apex_ext/apex_src/src/extensions/raw_content.h +20 -0
  131. data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +117 -3
  132. data/ext/apex_ext/apex_src/src/extensions/special_markers.c +53 -0
  133. data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +23 -1
  134. data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +83 -92
  135. data/ext/apex_ext/apex_src/src/extensions/toc.c +446 -25
  136. data/ext/apex_ext/apex_src/src/extensions/toc.h +26 -1
  137. data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +28 -0
  138. data/ext/apex_ext/apex_src/src/html_renderer.c +387 -17
  139. data/ext/apex_ext/apex_src/src/plugin_catalog.c +517 -0
  140. data/ext/apex_ext/apex_src/src/plugins_remote.c +88 -14
  141. data/ext/apex_ext/apex_src/src/plugins_remote.h +34 -0
  142. data/ext/apex_ext/apex_src/src/pretty_html.c +6 -1
  143. data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +21 -0
  144. data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +10 -0
  145. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +19 -0
  146. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +11 -0
  147. data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +2 -0
  148. data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +46 -0
  149. data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +5 -0
  150. data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +47 -0
  151. data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +3 -0
  152. data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +3 -0
  153. data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +8 -0
  154. data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +19 -0
  155. data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +5 -0
  156. data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +7 -0
  157. data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +81 -0
  158. data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +5 -0
  159. data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +47 -0
  160. data/ext/apex_ext/apex_src/tests/fixtures/speed.md +192 -0
  161. data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +37 -0
  162. data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +16 -0
  163. data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +301 -0
  164. data/ext/apex_ext/apex_src/tests/test_concordance.tsv +4 -0
  165. data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +82 -0
  166. data/ext/apex_ext/apex_src/tests/test_extensions.c +821 -1
  167. data/ext/apex_ext/apex_src/tests/test_ial.c +7 -0
  168. data/ext/apex_ext/apex_src/tests/test_marked_integration.c +25 -0
  169. data/ext/apex_ext/apex_src/tests/test_metadata.c +129 -0
  170. data/ext/apex_ext/apex_src/tests/test_output.c +568 -2
  171. data/ext/apex_ext/apex_src/tests/test_runner.c +11 -0
  172. data/ext/apex_ext/apex_src/tests/test_tables.c +420 -0
  173. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +1 -1
  174. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +77 -1
  175. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +135 -5
  176. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +56 -1
  177. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +38 -0
  178. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +1 -1
  179. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/cmark.cpython-314.pyc +0 -0
  180. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/normalize.cpython-314.pyc +0 -0
  181. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +69 -0
  182. data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +74 -0
  183. data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +22 -0
  184. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Apex +0 -0
  185. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Resources/Info.plist +44 -0
  186. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +128 -0
  187. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +5118 -0
  188. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  189. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  190. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/WorkspaceSettings.xcsettings +8 -0
  191. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +99 -0
  192. data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +452 -0
  193. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +52 -0
  194. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +55 -0
  195. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +2455 -0
  196. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  197. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +171 -0
  198. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +55 -0
  199. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +220 -0
  200. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +43 -0
  201. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +7 -0
  202. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +76 -0
  203. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +10 -0
  204. data/ext/apex_ext/extconf.rb +35 -0
  205. data/lib/apex/version.rb +2 -2
  206. metadata +117 -2
  207. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
@@ -0,0 +1,632 @@
1
+ /**
2
+ * Pandoc/Quarto fenced code block attributes ({.python filename="..."})
3
+ */
4
+
5
+ #include "code_fence_attrs.h"
6
+ #include <ctype.h>
7
+ #include <stdbool.h>
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+
12
+ static const char MARKER_PREFIX[] = "<!-- apex-code-fence-attrs ";
13
+ static const char MARKER_SUFFIX[] = " -->";
14
+
15
+ static const char *line_end_ptr(const char *p) {
16
+ const char *e = strchr(p, '\n');
17
+ return e ? e : p + strlen(p);
18
+ }
19
+
20
+ static bool append_chunk(char **out, size_t *len, size_t *cap, const char *chunk, size_t chunk_len) {
21
+ if (chunk_len == 0) {
22
+ return true;
23
+ }
24
+ if (*len + chunk_len + 1 > *cap) {
25
+ size_t new_cap = (*cap == 0 ? 256 : *cap * 2);
26
+ while (*len + chunk_len + 1 > new_cap) {
27
+ new_cap *= 2;
28
+ }
29
+ char *grown = realloc(*out, new_cap);
30
+ if (!grown) {
31
+ return false;
32
+ }
33
+ *out = grown;
34
+ *cap = new_cap;
35
+ }
36
+ memcpy(*out + *len, chunk, chunk_len);
37
+ *len += chunk_len;
38
+ (*out)[*len] = '\0';
39
+ return true;
40
+ }
41
+
42
+ static bool append_str(char **out, size_t *len, size_t *cap, const char *str) {
43
+ if (!str) {
44
+ return true;
45
+ }
46
+ return append_chunk(out, len, cap, str, strlen(str));
47
+ }
48
+
49
+ static void append_escaped_attr(char **buf, size_t *len, size_t *cap, const char *value) {
50
+ if (!value) {
51
+ return;
52
+ }
53
+ for (const char *p = value; *p; p++) {
54
+ if (*p == '"') {
55
+ append_str(buf, len, cap, "&quot;");
56
+ } else if (*p == '&') {
57
+ append_str(buf, len, cap, "&amp;");
58
+ } else {
59
+ append_chunk(buf, len, cap, p, 1);
60
+ }
61
+ }
62
+ }
63
+
64
+ static bool is_linenos_key(const char *key) {
65
+ return key && (strcasecmp(key, "linenos") == 0 ||
66
+ strcasecmp(key, "line-numbers") == 0 ||
67
+ strcasecmp(key, "line_numbers") == 0);
68
+ }
69
+
70
+ static bool is_linenos_class(const char *cls) {
71
+ return cls && (strcasecmp(cls, "numberLines") == 0 ||
72
+ strcasecmp(cls, "numberlines") == 0 ||
73
+ strcasecmp(cls, "line-numbers") == 0);
74
+ }
75
+
76
+ static bool truthy_value(const char *value) {
77
+ if (!value || !*value) {
78
+ return true;
79
+ }
80
+ return strcasecmp(value, "true") == 0 ||
81
+ strcasecmp(value, "yes") == 0 ||
82
+ strcasecmp(value, "1") == 0;
83
+ }
84
+
85
+ typedef struct {
86
+ char *id;
87
+ char **classes;
88
+ size_t class_count;
89
+ char **keys;
90
+ char **values;
91
+ size_t attr_count;
92
+ } parsed_attrs;
93
+
94
+ static void free_parsed_attrs(parsed_attrs *a) {
95
+ if (!a) {
96
+ return;
97
+ }
98
+ free(a->id);
99
+ if (a->classes) {
100
+ for (size_t i = 0; i < a->class_count; i++) {
101
+ free(a->classes[i]);
102
+ }
103
+ free(a->classes);
104
+ }
105
+ if (a->keys) {
106
+ for (size_t i = 0; i < a->attr_count; i++) {
107
+ free(a->keys[i]);
108
+ free(a->values[i]);
109
+ }
110
+ free(a->keys);
111
+ free(a->values);
112
+ }
113
+ memset(a, 0, sizeof(*a));
114
+ }
115
+
116
+ static bool push_class(parsed_attrs *a, char *class_name) {
117
+ if (!class_name || !*class_name) {
118
+ free(class_name);
119
+ return true;
120
+ }
121
+ char **grown = realloc(a->classes, (a->class_count + 1) * sizeof(char *));
122
+ if (!grown) {
123
+ free(class_name);
124
+ return false;
125
+ }
126
+ a->classes = grown;
127
+ a->classes[a->class_count++] = class_name;
128
+ return true;
129
+ }
130
+
131
+ static bool push_attr(parsed_attrs *a, char *key, char *value) {
132
+ if (!key) {
133
+ free(value);
134
+ return false;
135
+ }
136
+ char **new_keys = realloc(a->keys, (a->attr_count + 1) * sizeof(char *));
137
+ char **new_values = realloc(a->values, (a->attr_count + 1) * sizeof(char *));
138
+ if (!new_keys || !new_values) {
139
+ free(new_keys);
140
+ free(new_values);
141
+ free(key);
142
+ free(value);
143
+ return false;
144
+ }
145
+ a->keys = new_keys;
146
+ a->values = new_values;
147
+ a->keys[a->attr_count] = key;
148
+ a->values[a->attr_count] = value;
149
+ a->attr_count++;
150
+ return true;
151
+ }
152
+
153
+ static bool parse_braced_attrs(const char *info, size_t info_len, parsed_attrs *out) {
154
+ if (!info || info_len == 0 || !out) {
155
+ return false;
156
+ }
157
+
158
+ char *buffer = malloc(info_len + 1);
159
+ if (!buffer) {
160
+ return false;
161
+ }
162
+ memcpy(buffer, info, info_len);
163
+ buffer[info_len] = '\0';
164
+
165
+ char *p = buffer;
166
+ while (isspace((unsigned char)*p)) {
167
+ p++;
168
+ }
169
+ if (*p != '{') {
170
+ free(buffer);
171
+ return false;
172
+ }
173
+ p++;
174
+ if (*p == '=') {
175
+ free(buffer);
176
+ return false;
177
+ }
178
+
179
+ char *end = buffer + info_len;
180
+ while (end > p && isspace((unsigned char)*(end - 1))) {
181
+ end--;
182
+ }
183
+ if (end > p && *(end - 1) == '}') {
184
+ end--;
185
+ }
186
+ *end = '\0';
187
+
188
+ while (*p) {
189
+ while (isspace((unsigned char)*p)) {
190
+ p++;
191
+ }
192
+ if (!*p) {
193
+ break;
194
+ }
195
+
196
+ if (*p == '#') {
197
+ p++;
198
+ char *id_start = p;
199
+ while (*p && !isspace((unsigned char)*p) && *p != '.' && *p != '}') {
200
+ p++;
201
+ }
202
+ if (p > id_start) {
203
+ char saved = *p;
204
+ *p = '\0';
205
+ free(out->id);
206
+ out->id = strdup(id_start);
207
+ *p = saved;
208
+ }
209
+ continue;
210
+ }
211
+
212
+ if (*p == '.') {
213
+ p++;
214
+ char *class_start = p;
215
+ while (*p && !isspace((unsigned char)*p) && *p != '.' && *p != '#' && *p != '}') {
216
+ p++;
217
+ }
218
+ if (p > class_start) {
219
+ char saved = *p;
220
+ *p = '\0';
221
+ if (!push_class(out, strdup(class_start))) {
222
+ free(buffer);
223
+ return false;
224
+ }
225
+ *p = saved;
226
+ }
227
+ continue;
228
+ }
229
+
230
+ char *key_start = p;
231
+ while (*p && *p != '=' && !isspace((unsigned char)*p) && *p != '}') {
232
+ p++;
233
+ }
234
+ if (*p != '=') {
235
+ p++;
236
+ continue;
237
+ }
238
+ char saved = *p;
239
+ *p = '\0';
240
+ char *key = strdup(key_start);
241
+ *p = saved;
242
+ p++;
243
+
244
+ char *value = NULL;
245
+ if (*p == '"' || *p == '\'') {
246
+ char quote = *p++;
247
+ char *value_start = p;
248
+ while (*p && *p != quote) {
249
+ if (*p == '\\' && *(p + 1)) {
250
+ p++;
251
+ }
252
+ p++;
253
+ }
254
+ if (*p == quote) {
255
+ *p = '\0';
256
+ value = strdup(value_start);
257
+ p++;
258
+ }
259
+ } else {
260
+ char *value_start = p;
261
+ while (*p && !isspace((unsigned char)*p) && *p != '}') {
262
+ p++;
263
+ }
264
+ char saved_val = *p;
265
+ *p = '\0';
266
+ value = strdup(value_start);
267
+ *p = saved_val;
268
+ }
269
+
270
+ if (!push_attr(out, key, value)) {
271
+ free(buffer);
272
+ return false;
273
+ }
274
+ }
275
+
276
+ free(buffer);
277
+ return out->class_count > 0 || out->attr_count > 0 || out->id;
278
+ }
279
+
280
+ static char *build_marker_from_parsed(const parsed_attrs *parsed) {
281
+ char *marker_body = NULL;
282
+ size_t len = 0;
283
+ size_t cap = 0;
284
+
285
+ const char *language = NULL;
286
+ size_t extra_class_start = 0;
287
+
288
+ for (size_t i = 0; i < parsed->class_count; i++) {
289
+ if (is_linenos_class(parsed->classes[i])) {
290
+ append_str(&marker_body, &len, &cap, "data-linenos=\"true\" ");
291
+ continue;
292
+ }
293
+ if (!language) {
294
+ language = parsed->classes[i];
295
+ continue;
296
+ }
297
+ if (extra_class_start == 0) {
298
+ append_str(&marker_body, &len, &cap, "class=\"");
299
+ } else {
300
+ append_str(&marker_body, &len, &cap, " ");
301
+ }
302
+ append_escaped_attr(&marker_body, &len, &cap, parsed->classes[i]);
303
+ extra_class_start++;
304
+ }
305
+ if (extra_class_start > 0) {
306
+ append_str(&marker_body, &len, &cap, "\" ");
307
+ }
308
+
309
+ if (parsed->id) {
310
+ append_str(&marker_body, &len, &cap, "id=\"");
311
+ append_escaped_attr(&marker_body, &len, &cap, parsed->id);
312
+ append_str(&marker_body, &len, &cap, "\" ");
313
+ }
314
+
315
+ for (size_t i = 0; i < parsed->attr_count; i++) {
316
+ const char *key = parsed->keys[i];
317
+ const char *value = parsed->values[i];
318
+ if (is_linenos_key(key)) {
319
+ if (truthy_value(value)) {
320
+ append_str(&marker_body, &len, &cap, "data-linenos=\"true\" ");
321
+ }
322
+ continue;
323
+ }
324
+ char attr_name[128];
325
+ if (strcasecmp(key, "filename") == 0) {
326
+ snprintf(attr_name, sizeof(attr_name), "data-filename");
327
+ } else if (strncmp(key, "data-", 5) == 0) {
328
+ snprintf(attr_name, sizeof(attr_name), "%s", key);
329
+ } else {
330
+ snprintf(attr_name, sizeof(attr_name), "data-%s", key);
331
+ }
332
+ append_str(&marker_body, &len, &cap, attr_name);
333
+ append_str(&marker_body, &len, &cap, "=\"");
334
+ append_escaped_attr(&marker_body, &len, &cap, value ? value : "");
335
+ append_str(&marker_body, &len, &cap, "\" ");
336
+ }
337
+
338
+ if (!marker_body || len == 0) {
339
+ free(marker_body);
340
+ if (!language) {
341
+ return NULL;
342
+ }
343
+ marker_body = strdup(" ");
344
+ return marker_body;
345
+ }
346
+
347
+ (void)language;
348
+ return marker_body;
349
+ }
350
+
351
+ static bool try_transform_open_fence(const char *line_start, const char *line_end,
352
+ char **out, size_t *len, size_t *cap, bool *changed) {
353
+ const char *p = line_start;
354
+ while (p < line_end && (*p == ' ' || *p == '\t')) {
355
+ p++;
356
+ }
357
+ if ((size_t)(line_end - p) < 3 || strncmp(p, "```", 3) != 0) {
358
+ return false;
359
+ }
360
+
361
+ int tick_count = 0;
362
+ const char *ticks = p;
363
+ while (ticks < line_end && *ticks == '`') {
364
+ tick_count++;
365
+ ticks++;
366
+ }
367
+ if (tick_count < 3) {
368
+ return false;
369
+ }
370
+ while (ticks < line_end && (*ticks == ' ' || *ticks == '\t')) {
371
+ ticks++;
372
+ }
373
+ if (ticks >= line_end) {
374
+ return false;
375
+ }
376
+
377
+ const char *info_start = ticks;
378
+ const char *info_end = line_end;
379
+ while (info_end > info_start && (info_end[-1] == ' ' || info_end[-1] == '\t')) {
380
+ info_end--;
381
+ }
382
+ if (info_end <= info_start || *info_start != '{') {
383
+ return false;
384
+ }
385
+
386
+ parsed_attrs parsed = {0};
387
+ if (!parse_braced_attrs(info_start, (size_t)(info_end - info_start), &parsed)) {
388
+ free_parsed_attrs(&parsed);
389
+ return false;
390
+ }
391
+
392
+ char *language = NULL;
393
+ for (size_t i = 0; i < parsed.class_count; i++) {
394
+ if (!is_linenos_class(parsed.classes[i])) {
395
+ language = strdup(parsed.classes[i]);
396
+ break;
397
+ }
398
+ }
399
+
400
+ char *marker_body = build_marker_from_parsed(&parsed);
401
+ free_parsed_attrs(&parsed);
402
+ if (!marker_body && !language) {
403
+ free(language);
404
+ return false;
405
+ }
406
+
407
+ if (marker_body) {
408
+ char *trim = marker_body;
409
+ while (*trim == ' ') {
410
+ trim++;
411
+ }
412
+ if (*trim) {
413
+ if (!append_str(out, len, cap, MARKER_PREFIX) ||
414
+ !append_str(out, len, cap, marker_body) ||
415
+ !append_str(out, len, cap, MARKER_SUFFIX) ||
416
+ !append_str(out, len, cap, "\n")) {
417
+ free(marker_body);
418
+ free(language);
419
+ return false;
420
+ }
421
+ }
422
+ }
423
+ free(marker_body);
424
+
425
+ if (!append_chunk(out, len, cap, line_start, (size_t)(p - line_start))) {
426
+ return false;
427
+ }
428
+ for (int i = 0; i < tick_count; i++) {
429
+ if (!append_str(out, len, cap, "`")) {
430
+ return false;
431
+ }
432
+ }
433
+ if (language && *language) {
434
+ if (!append_str(out, len, cap, language)) {
435
+ free(language);
436
+ return false;
437
+ }
438
+ }
439
+ free(language);
440
+ if (line_end < line_start + strlen(line_start) && *line_end == '\n') {
441
+ if (!append_str(out, len, cap, "\n")) {
442
+ return false;
443
+ }
444
+ }
445
+
446
+ *changed = true;
447
+ return true;
448
+ }
449
+
450
+ char *apex_preprocess_code_fence_attrs(const char *text) {
451
+ if (!text) {
452
+ return NULL;
453
+ }
454
+
455
+ size_t cap = strlen(text) * 2 + 256;
456
+ char *out = malloc(cap);
457
+ if (!out) {
458
+ return NULL;
459
+ }
460
+ size_t len = 0;
461
+ out[0] = '\0';
462
+ bool changed = false;
463
+ bool in_fenced_code = false;
464
+
465
+ const char *read = text;
466
+ while (*read) {
467
+ const char *line_start = read;
468
+ const char *line_end = line_end_ptr(read);
469
+ bool has_newline = (*line_end == '\n');
470
+ bool at_line_start = (read == text || read[-1] == '\n');
471
+
472
+ if (!in_fenced_code && at_line_start &&
473
+ try_transform_open_fence(line_start, line_end, &out, &len, &cap, &changed)) {
474
+ read = has_newline ? line_end + 1 : line_end;
475
+ in_fenced_code = true;
476
+ continue;
477
+ }
478
+
479
+ if (at_line_start) {
480
+ const char *content = line_start;
481
+ while (content < line_end && (*content == ' ' || *content == '\t')) {
482
+ content++;
483
+ }
484
+ if ((size_t)(line_end - content) >= 3 && strncmp(content, "```", 3) == 0) {
485
+ in_fenced_code = !in_fenced_code;
486
+ }
487
+ }
488
+
489
+ size_t line_len = (size_t)(line_end - line_start) + (has_newline ? 1 : 0);
490
+ if (!append_chunk(&out, &len, &cap, line_start, line_len)) {
491
+ free(out);
492
+ return NULL;
493
+ }
494
+ read = has_newline ? line_end + 1 : line_end;
495
+ }
496
+
497
+ if (!changed) {
498
+ free(out);
499
+ return NULL;
500
+ }
501
+ return out;
502
+ }
503
+
504
+ char *apex_postprocess_code_fence_attrs_html(const char *html) {
505
+ if (!html || !strstr(html, MARKER_PREFIX)) {
506
+ return NULL;
507
+ }
508
+
509
+ size_t cap = strlen(html) + 1024;
510
+ char *out = malloc(cap);
511
+ if (!out) {
512
+ return NULL;
513
+ }
514
+ size_t len = 0;
515
+ out[0] = '\0';
516
+ bool changed = false;
517
+
518
+ const char *read = html;
519
+ const size_t prefix_len = sizeof(MARKER_PREFIX) - 1;
520
+ const size_t suffix_len = sizeof(MARKER_SUFFIX) - 1;
521
+
522
+ while (*read) {
523
+ const char *marker = strstr(read, MARKER_PREFIX);
524
+ if (!marker) {
525
+ break;
526
+ }
527
+
528
+ size_t prefix_copy = (size_t)(marker - read);
529
+ if (len + prefix_copy + 1 > cap) {
530
+ cap = (len + prefix_copy + 1) * 2;
531
+ char *grown = realloc(out, cap);
532
+ if (!grown) {
533
+ free(out);
534
+ return NULL;
535
+ }
536
+ out = grown;
537
+ }
538
+ if (prefix_copy > 0) {
539
+ memcpy(out + len, read, prefix_copy);
540
+ len += prefix_copy;
541
+ }
542
+
543
+ const char *attrs_start = marker + prefix_len;
544
+ const char *attrs_end = strstr(attrs_start, MARKER_SUFFIX);
545
+ if (!attrs_end) {
546
+ read = marker + prefix_len;
547
+ continue;
548
+ }
549
+
550
+ size_t attrs_len = (size_t)(attrs_end - attrs_start);
551
+ const char *scan = attrs_end + suffix_len;
552
+ while (*scan == ' ' || *scan == '\t' || *scan == '\n' || *scan == '\r') {
553
+ scan++;
554
+ }
555
+ if (strncmp(scan, "<p>", 3) == 0) {
556
+ scan += 3;
557
+ while (*scan == ' ' || *scan == '\t' || *scan == '\n' || *scan == '\r') {
558
+ scan++;
559
+ }
560
+ }
561
+
562
+ const char *target = NULL;
563
+ const char *candidates[] = {
564
+ "<div class=\"highlight\"",
565
+ "<div class=\"sourceCode\"",
566
+ "<pre",
567
+ NULL
568
+ };
569
+ for (size_t i = 0; candidates[i]; i++) {
570
+ const char *found = strstr(scan, candidates[i]);
571
+ if (found && (!target || found < target)) {
572
+ target = found;
573
+ }
574
+ }
575
+ if (!target || target > scan + 512) {
576
+ read = attrs_end + suffix_len;
577
+ changed = true;
578
+ continue;
579
+ }
580
+
581
+ const char *tag_end = strchr(target, '>');
582
+ if (!tag_end) {
583
+ read = attrs_end + suffix_len;
584
+ changed = true;
585
+ continue;
586
+ }
587
+
588
+ size_t tag_len = (size_t)(tag_end - target);
589
+ if (len + tag_len + attrs_len + 16 > cap) {
590
+ cap = (len + tag_len + attrs_len + 16) * 2;
591
+ char *grown = realloc(out, cap);
592
+ if (!grown) {
593
+ free(out);
594
+ return NULL;
595
+ }
596
+ out = grown;
597
+ }
598
+
599
+ memcpy(out + len, target, tag_len);
600
+ len += tag_len;
601
+
602
+ if (attrs_len > 0) {
603
+ out[len++] = ' ';
604
+ memcpy(out + len, attrs_start, attrs_len);
605
+ len += attrs_len;
606
+ }
607
+
608
+ out[len++] = '>';
609
+ read = tag_end + 1;
610
+ changed = true;
611
+ }
612
+
613
+ if (!changed) {
614
+ free(out);
615
+ return NULL;
616
+ }
617
+
618
+ size_t tail_len = strlen(read);
619
+ if (len + tail_len + 1 > cap) {
620
+ cap = len + tail_len + 2;
621
+ char *grown = realloc(out, cap);
622
+ if (!grown) {
623
+ free(out);
624
+ return NULL;
625
+ }
626
+ out = grown;
627
+ }
628
+ memcpy(out + len, read, tail_len);
629
+ len += tail_len;
630
+ out[len] = '\0';
631
+ return out;
632
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pandoc/Quarto fenced code block attributes ({.python filename="..."})
3
+ */
4
+
5
+ #ifndef APEX_CODE_FENCE_ATTRS_H
6
+ #define APEX_CODE_FENCE_ATTRS_H
7
+
8
+ #include <stdbool.h>
9
+
10
+ /**
11
+ * Normalize ```{.class key="val"} fences to plain language fences and emit
12
+ * HTML comment markers for preserved attributes. Returns NULL if unchanged.
13
+ */
14
+ char *apex_preprocess_code_fence_attrs(const char *text);
15
+
16
+ /**
17
+ * Apply preserved fence attributes to the following <pre> tag and strip markers.
18
+ * Returns NULL if unchanged.
19
+ */
20
+ char *apex_postprocess_code_fence_attrs_html(const char *html);
21
+
22
+ #endif /* APEX_CODE_FENCE_ATTRS_H */