apex-ruby 1.0.17 → 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
@@ -13,6 +13,534 @@
13
13
  #include <ctype.h>
14
14
  #include <stdbool.h>
15
15
 
16
+ typedef struct {
17
+ char **ids;
18
+ size_t count;
19
+ size_t capacity;
20
+ } ref_id_list;
21
+
22
+ static void ref_id_list_free(ref_id_list *list) {
23
+ if (!list) return;
24
+ if (list->ids) {
25
+ for (size_t i = 0; i < list->count; i++) {
26
+ free(list->ids[i]);
27
+ }
28
+ free(list->ids);
29
+ }
30
+ list->ids = NULL;
31
+ list->count = 0;
32
+ list->capacity = 0;
33
+ }
34
+
35
+ static bool reference_id_matches(const char *ref_id, const char *text, size_t text_len) {
36
+ if (!ref_id || !text) return false;
37
+
38
+ const char *p = ref_id;
39
+ const char *t = text;
40
+ size_t remaining = text_len;
41
+
42
+ while (*p && isspace((unsigned char)*p)) p++;
43
+ while (remaining > 0 && isspace((unsigned char)*t)) {
44
+ t++;
45
+ remaining--;
46
+ }
47
+
48
+ while (*p && remaining > 0) {
49
+ if (tolower((unsigned char)*p) != tolower((unsigned char)*t)) {
50
+ if (isspace((unsigned char)*p) && isspace((unsigned char)*t)) {
51
+ while (*p && isspace((unsigned char)*p)) p++;
52
+ while (remaining > 0 && isspace((unsigned char)*t)) {
53
+ t++;
54
+ remaining--;
55
+ }
56
+ continue;
57
+ }
58
+ return false;
59
+ }
60
+ p++;
61
+ t++;
62
+ remaining--;
63
+ }
64
+
65
+ while (*p && isspace((unsigned char)*p)) p++;
66
+ while (remaining > 0 && isspace((unsigned char)*t)) {
67
+ t++;
68
+ remaining--;
69
+ }
70
+
71
+ return (*p == '\0' && remaining == 0);
72
+ }
73
+
74
+ static bool ref_id_list_contains(ref_id_list *list, const char *id, size_t id_len) {
75
+ for (size_t i = 0; i < list->count; i++) {
76
+ if (reference_id_matches(list->ids[i], id, id_len)) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+
83
+ static void ref_id_list_add(ref_id_list *list, const char *id, size_t id_len) {
84
+ if (!list || !id || id_len == 0) return;
85
+ if (ref_id_list_contains(list, id, id_len)) return;
86
+
87
+ char *copy = malloc(id_len + 1);
88
+ if (!copy) return;
89
+ memcpy(copy, id, id_len);
90
+ copy[id_len] = '\0';
91
+
92
+ if (list->count >= list->capacity) {
93
+ size_t new_capacity = list->capacity ? list->capacity * 2 : 16;
94
+ char **new_ids = realloc(list->ids, new_capacity * sizeof(char *));
95
+ if (!new_ids) {
96
+ free(copy);
97
+ return;
98
+ }
99
+ list->ids = new_ids;
100
+ list->capacity = new_capacity;
101
+ }
102
+
103
+ list->ids[list->count++] = copy;
104
+ }
105
+
106
+ static void collect_used_reference_ids(const char *text, ref_id_list *list) {
107
+ if (!text || !list) return;
108
+
109
+ bool in_code_block = false;
110
+ bool in_inline_code = false;
111
+ const char *p = text;
112
+
113
+ while (*p) {
114
+ if (!in_code_block && !in_inline_code && *p == '`') {
115
+ int backtick_count = 1;
116
+ const char *q = p + 1;
117
+ while (*q == '`') {
118
+ backtick_count++;
119
+ q++;
120
+ }
121
+ if (backtick_count >= 3) {
122
+ in_code_block = !in_code_block;
123
+ } else {
124
+ in_inline_code = !in_inline_code;
125
+ }
126
+ p = q;
127
+ continue;
128
+ }
129
+
130
+ if (!in_code_block && !in_inline_code && *p == '[') {
131
+ const char *label_start = p + 1;
132
+ const char *label_end = strchr(label_start, ']');
133
+ if (label_end && label_end[1] == '[') {
134
+ const char *ref_start = label_end + 2;
135
+ const char *ref_end = strchr(ref_start, ']');
136
+ if (ref_end) {
137
+ if (ref_start == ref_end) {
138
+ ref_id_list_add(list, label_start, (size_t)(label_end - label_start));
139
+ } else {
140
+ ref_id_list_add(list, ref_start, (size_t)(ref_end - ref_start));
141
+ }
142
+ p = ref_end + 1;
143
+ continue;
144
+ }
145
+ }
146
+ }
147
+
148
+ p++;
149
+ }
150
+ }
151
+
152
+ static void collect_used_footnote_ids(const char *text, ref_id_list *list) {
153
+ if (!text || !list) return;
154
+
155
+ bool in_code_block = false;
156
+ bool in_inline_code = false;
157
+ const char *p = text;
158
+
159
+ while (*p) {
160
+ if (!in_code_block && !in_inline_code && *p == '`') {
161
+ int backtick_count = 1;
162
+ const char *q = p + 1;
163
+ while (*q == '`') {
164
+ backtick_count++;
165
+ q++;
166
+ }
167
+ if (backtick_count >= 3) {
168
+ in_code_block = !in_code_block;
169
+ } else {
170
+ in_inline_code = !in_inline_code;
171
+ }
172
+ p = q;
173
+ continue;
174
+ }
175
+
176
+ if (!in_code_block && !in_inline_code && *p == '[' && p[1] == '^') {
177
+ const char *id_start = p + 1;
178
+ const char *id_end = strchr(p + 2, ']');
179
+ if (id_end) {
180
+ const char *line_start = p;
181
+ while (line_start > text && line_start[-1] != '\n') {
182
+ line_start--;
183
+ }
184
+ const char *ws = line_start;
185
+ while (ws < p && (*ws == ' ' || *ws == '\t')) {
186
+ ws++;
187
+ }
188
+ if (ws == p && id_end[1] == ':') {
189
+ p = id_end + 1;
190
+ continue;
191
+ }
192
+
193
+ ref_id_list_add(list, id_start, (size_t)(id_end - id_start));
194
+ p = id_end + 1;
195
+ continue;
196
+ }
197
+ }
198
+
199
+ p++;
200
+ }
201
+ }
202
+
203
+ static void collect_defined_reference_ids(const char *text, ref_id_list *list) {
204
+ if (!text || !list) return;
205
+
206
+ const char *p = text;
207
+ while (*p) {
208
+ const char *line_start = p;
209
+ const char *line_end = strchr(p, '\n');
210
+ if (!line_end) line_end = p + strlen(p);
211
+
212
+ const char *content_start = line_start;
213
+ while (content_start < line_end && (*content_start == ' ' || *content_start == '\t')) {
214
+ content_start++;
215
+ }
216
+
217
+ if (content_start < line_end && *content_start == '[') {
218
+ const char *id_end = strchr(content_start + 1, ']');
219
+ if (id_end && id_end < line_end && id_end[1] == ':') {
220
+ ref_id_list_add(list, content_start + 1, (size_t)(id_end - (content_start + 1)));
221
+ }
222
+ }
223
+
224
+ p = (*line_end == '\n') ? line_end + 1 : line_end;
225
+ }
226
+ }
227
+
228
+ static char *extract_matching_reference_definitions(const char *full_doc, ref_id_list *needed) {
229
+ if (!full_doc || !needed || needed->count == 0) return NULL;
230
+
231
+ size_t capacity = 256;
232
+ size_t len = 0;
233
+ char *output = malloc(capacity);
234
+ if (!output) return NULL;
235
+
236
+ const char *p = full_doc;
237
+ while (*p) {
238
+ const char *line_start = p;
239
+ const char *line_end = strchr(p, '\n');
240
+ if (!line_end) line_end = p + strlen(p);
241
+ size_t line_len = (size_t)(line_end - line_start);
242
+
243
+ const char *content_start = line_start;
244
+ while (content_start < line_end && (*content_start == ' ' || *content_start == '\t')) {
245
+ content_start++;
246
+ }
247
+
248
+ if (content_start < line_end && *content_start == '[') {
249
+ const char *id_end = strchr(content_start + 1, ']');
250
+ if (id_end && id_end < line_end && id_end[1] == ':') {
251
+ const char *id_start = content_start + 1;
252
+ size_t id_len = (size_t)(id_end - id_start);
253
+ bool needed_line = false;
254
+ for (size_t i = 0; i < needed->count; i++) {
255
+ if (reference_id_matches(needed->ids[i], id_start, id_len)) {
256
+ needed_line = true;
257
+ break;
258
+ }
259
+ }
260
+
261
+ if (needed_line) {
262
+ if (len + line_len + 2 > capacity) {
263
+ capacity = (len + line_len + 2) * 2;
264
+ char *new_output = realloc(output, capacity);
265
+ if (!new_output) {
266
+ free(output);
267
+ return NULL;
268
+ }
269
+ output = new_output;
270
+ }
271
+ if (len > 0) {
272
+ output[len++] = '\n';
273
+ }
274
+ memcpy(output + len, line_start, line_len);
275
+ len += line_len;
276
+ }
277
+ }
278
+ }
279
+
280
+ p = (*line_end == '\n') ? line_end + 1 : line_end;
281
+ }
282
+
283
+ if (len == 0) {
284
+ free(output);
285
+ return NULL;
286
+ }
287
+
288
+ output[len] = '\0';
289
+ return output;
290
+ }
291
+
292
+ static bool is_footnote_definition_line(const char *line_start, const char *line_end, const char *footnote_id) {
293
+ const char *content_start = line_start;
294
+ while (content_start < line_end && (*content_start == ' ' || *content_start == '\t')) {
295
+ content_start++;
296
+ }
297
+
298
+ if (content_start >= line_end || *content_start != '[') {
299
+ return false;
300
+ }
301
+
302
+ const char *id_end = strchr(content_start + 1, ']');
303
+ if (!id_end || id_end >= line_end || id_end[1] != ':') {
304
+ return false;
305
+ }
306
+
307
+ return reference_id_matches(footnote_id, content_start + 1, (size_t)(id_end - (content_start + 1)));
308
+ }
309
+
310
+ static bool is_footnote_definition_continuation(const char *line_start, size_t line_len) {
311
+ if (line_len == 0) {
312
+ return true;
313
+ }
314
+ if (*line_start == '\t') {
315
+ return true;
316
+ }
317
+ return line_len >= 4 && line_start[0] == ' ' && line_start[1] == ' ' &&
318
+ line_start[2] == ' ' && line_start[3] == ' ';
319
+ }
320
+
321
+ static char *extract_footnote_definition_block(const char *full_doc, const char *footnote_id) {
322
+ if (!full_doc || !footnote_id) return NULL;
323
+
324
+ const char *p = full_doc;
325
+ while (*p) {
326
+ const char *line_start = p;
327
+ const char *line_end = strchr(p, '\n');
328
+ if (!line_end) line_end = p + strlen(p);
329
+ size_t line_len = (size_t)(line_end - line_start);
330
+
331
+ if (is_footnote_definition_line(line_start, line_end, footnote_id)) {
332
+ size_t capacity = line_len + 64;
333
+ size_t len = 0;
334
+ char *output = malloc(capacity);
335
+ if (!output) return NULL;
336
+
337
+ if (len + line_len + 2 > capacity) {
338
+ capacity = line_len + 2;
339
+ char *new_output = realloc(output, capacity);
340
+ if (!new_output) {
341
+ free(output);
342
+ return NULL;
343
+ }
344
+ output = new_output;
345
+ }
346
+ memcpy(output + len, line_start, line_len);
347
+ len += line_len;
348
+
349
+ p = (*line_end == '\n') ? line_end + 1 : line_end;
350
+ while (*p) {
351
+ line_start = p;
352
+ line_end = strchr(p, '\n');
353
+ if (!line_end) line_end = p + strlen(p);
354
+ line_len = (size_t)(line_end - line_start);
355
+
356
+ if (!is_footnote_definition_continuation(line_start, line_len)) {
357
+ break;
358
+ }
359
+
360
+ if (len + line_len + 2 > capacity) {
361
+ capacity = (len + line_len + 2) * 2;
362
+ char *new_output = realloc(output, capacity);
363
+ if (!new_output) {
364
+ free(output);
365
+ return NULL;
366
+ }
367
+ output = new_output;
368
+ }
369
+ output[len++] = '\n';
370
+ memcpy(output + len, line_start, line_len);
371
+ len += line_len;
372
+
373
+ p = (*line_end == '\n') ? line_end + 1 : line_end;
374
+ }
375
+
376
+ output[len] = '\0';
377
+ return output;
378
+ }
379
+
380
+ p = (*line_end == '\n') ? line_end + 1 : line_end;
381
+ }
382
+
383
+ return NULL;
384
+ }
385
+
386
+ static char *extract_matching_footnote_definitions(const char *full_doc, ref_id_list *needed) {
387
+ if (!full_doc || !needed || needed->count == 0) return NULL;
388
+
389
+ size_t capacity = 256;
390
+ size_t len = 0;
391
+ char *output = malloc(capacity);
392
+ if (!output) return NULL;
393
+
394
+ for (size_t i = 0; i < needed->count; i++) {
395
+ if (needed->ids[i][0] != '^') {
396
+ continue;
397
+ }
398
+
399
+ char *block = extract_footnote_definition_block(full_doc, needed->ids[i]);
400
+ if (!block) continue;
401
+
402
+ size_t block_len = strlen(block);
403
+ if (len > 0) {
404
+ if (len + 1 >= capacity) {
405
+ capacity = (len + block_len + 2) * 2;
406
+ char *new_output = realloc(output, capacity);
407
+ if (!new_output) {
408
+ free(block);
409
+ free(output);
410
+ return NULL;
411
+ }
412
+ output = new_output;
413
+ }
414
+ output[len++] = '\n';
415
+ }
416
+
417
+ if (len + block_len + 2 > capacity) {
418
+ capacity = (len + block_len + 2) * 2;
419
+ char *new_output = realloc(output, capacity);
420
+ if (!new_output) {
421
+ free(block);
422
+ free(output);
423
+ return NULL;
424
+ }
425
+ output = new_output;
426
+ }
427
+
428
+ memcpy(output + len, block, block_len);
429
+ len += block_len;
430
+ free(block);
431
+ }
432
+
433
+ if (len == 0) {
434
+ free(output);
435
+ return NULL;
436
+ }
437
+
438
+ output[len] = '\0';
439
+ return output;
440
+ }
441
+
442
+ static ref_id_list filter_needed_ids(ref_id_list *used, ref_id_list *defined_in_content, bool footnotes_only) {
443
+ ref_id_list needed = {0};
444
+ for (size_t i = 0; i < used->count; i++) {
445
+ bool is_footnote = (used->ids[i][0] == '^');
446
+ if (footnotes_only != is_footnote) {
447
+ continue;
448
+ }
449
+
450
+ bool already_defined = false;
451
+ for (size_t j = 0; j < defined_in_content->count; j++) {
452
+ if (reference_id_matches(used->ids[i], defined_in_content->ids[j],
453
+ strlen(defined_in_content->ids[j]))) {
454
+ already_defined = true;
455
+ break;
456
+ }
457
+ }
458
+ if (!already_defined) {
459
+ ref_id_list_add(&needed, used->ids[i], strlen(used->ids[i]));
460
+ }
461
+ }
462
+ return needed;
463
+ }
464
+
465
+ /**
466
+ * Prepend reference and footnote definitions from the full document that are
467
+ * used in content but not already defined within content.
468
+ * Caller must free the returned string.
469
+ */
470
+ static char *prepend_needed_definitions(const char *full_doc, const char *content) {
471
+ if (!full_doc || !content || strcmp(full_doc, content) == 0) {
472
+ return NULL;
473
+ }
474
+
475
+ ref_id_list used = {0};
476
+ ref_id_list defined_in_content = {0};
477
+ collect_used_reference_ids(content, &used);
478
+ collect_used_footnote_ids(content, &used);
479
+ if (used.count == 0) {
480
+ return NULL;
481
+ }
482
+
483
+ collect_defined_reference_ids(content, &defined_in_content);
484
+
485
+ ref_id_list needed_links = filter_needed_ids(&used, &defined_in_content, false);
486
+ ref_id_list needed_footnotes = filter_needed_ids(&used, &defined_in_content, true);
487
+
488
+ ref_id_list_free(&used);
489
+ ref_id_list_free(&defined_in_content);
490
+
491
+ char *link_defs = needed_links.count > 0 ?
492
+ extract_matching_reference_definitions(full_doc, &needed_links) : NULL;
493
+ char *footnote_defs = needed_footnotes.count > 0 ?
494
+ extract_matching_footnote_definitions(full_doc, &needed_footnotes) : NULL;
495
+
496
+ ref_id_list_free(&needed_links);
497
+ ref_id_list_free(&needed_footnotes);
498
+
499
+ if (!link_defs && !footnote_defs) {
500
+ free(link_defs);
501
+ free(footnote_defs);
502
+ return NULL;
503
+ }
504
+
505
+ size_t defs_len = 0;
506
+ if (link_defs) defs_len += strlen(link_defs);
507
+ if (footnote_defs) {
508
+ if (defs_len > 0) defs_len++;
509
+ defs_len += strlen(footnote_defs);
510
+ }
511
+
512
+ size_t content_len = strlen(content);
513
+ char *result = malloc(defs_len + 2 + content_len + 1);
514
+ if (!result) {
515
+ free(link_defs);
516
+ free(footnote_defs);
517
+ return NULL;
518
+ }
519
+
520
+ size_t offset = 0;
521
+ if (link_defs) {
522
+ size_t link_len = strlen(link_defs);
523
+ memcpy(result + offset, link_defs, link_len);
524
+ offset += link_len;
525
+ free(link_defs);
526
+ }
527
+ if (footnote_defs) {
528
+ if (offset > 0) {
529
+ result[offset++] = '\n';
530
+ }
531
+ size_t fn_len = strlen(footnote_defs);
532
+ memcpy(result + offset, footnote_defs, fn_len);
533
+ offset += fn_len;
534
+ free(footnote_defs);
535
+ }
536
+
537
+ result[offset++] = '\n';
538
+ result[offset++] = '\n';
539
+ memcpy(result + offset, content, content_len);
540
+ result[offset + content_len] = '\0';
541
+ return result;
542
+ }
543
+
16
544
  /**
17
545
  * Find the next HTML tag with markdown attribute
18
546
  * Returns position of '<' or NULL if not found
@@ -142,9 +670,11 @@ static const char *find_closing_tag(const char *text, const char *tag_name) {
142
670
  /**
143
671
  * Process HTML tags with markdown attributes
144
672
  * If img_attrs is non-NULL, image attributes (e.g. width/height from ref defs) are applied to images in markdown="1" regions.
673
+ * full_doc is the original document used to resolve reference-style link definitions.
145
674
  */
146
- char *apex_process_html_markdown(const char *text, void *img_attrs) {
675
+ static char *apex_process_html_markdown_impl(const char *text, void *img_attrs, const char *full_doc) {
147
676
  if (!text) return NULL;
677
+ if (!full_doc) full_doc = text;
148
678
 
149
679
  image_attr_entry *attrs = (image_attr_entry *)img_attrs;
150
680
 
@@ -157,6 +687,22 @@ char *apex_process_html_markdown(const char *text, void *img_attrs) {
157
687
  char *write_pos = output;
158
688
  size_t remaining = output_capacity;
159
689
 
690
+ #define ENSURE_OUTPUT_SPACE(needed) do { \
691
+ if ((needed) > remaining) { \
692
+ size_t used = (size_t)(write_pos - output); \
693
+ size_t min_capacity = used + (needed) + 1; \
694
+ output_capacity = (min_capacity < 1024) ? 2048 : min_capacity * 2; \
695
+ char *new_output = realloc(output, output_capacity + 1); \
696
+ if (!new_output) { \
697
+ free(output); \
698
+ return NULL; \
699
+ } \
700
+ output = new_output; \
701
+ write_pos = output + used; \
702
+ remaining = output_capacity - used; \
703
+ } \
704
+ } while (0)
705
+
160
706
  while (*read_pos) {
161
707
  char tag_name[64];
162
708
  char markdown_attr[64];
@@ -236,29 +782,33 @@ char *apex_process_html_markdown(const char *text, void *img_attrs) {
236
782
 
237
783
  /* Recursively process nested divs with markdown="1" BEFORE parsing */
238
784
  /* This ensures nested divs are processed before cmark-gfm sees them */
239
- char *processed_content = apex_process_html_markdown(content, img_attrs);
785
+ char *processed_content = apex_process_html_markdown_impl(content, img_attrs, full_doc);
240
786
  if (processed_content) {
241
787
  free(content);
242
788
  content = processed_content;
243
789
  content_len = strlen(content);
244
790
  }
245
791
 
792
+ char *content_with_refs = prepend_needed_definitions(full_doc, content);
793
+ if (content_with_refs) {
794
+ free(content);
795
+ content = content_with_refs;
796
+ content_len = strlen(content);
797
+ }
798
+
246
799
  /* Create parser and parse */
247
800
  /* Use CMARK_OPT_UNSAFE to allow raw HTML (including nested divs) */
248
- int cmark_opts = CMARK_OPT_DEFAULT | CMARK_OPT_UNSAFE;
801
+ int cmark_opts = CMARK_OPT_DEFAULT | CMARK_OPT_UNSAFE | CMARK_OPT_FOOTNOTES;
249
802
  cmark_parser *parser = cmark_parser_new(cmark_opts);
250
803
  if (parser) {
251
804
  cmark_parser_feed(parser, content, content_len);
252
805
  cmark_node *doc = cmark_parser_finish(parser);
253
806
 
254
807
  if (doc) {
255
- /* Process IAL in inner content so block/span IAL (e.g. {: .lead }) are applied */
256
808
  apex_process_ial_in_tree(doc, NULL);
257
- /* Apply image attributes (e.g. width/height from ref defs) so images in fenced divs get them */
258
809
  if (attrs) {
259
810
  apex_apply_image_attributes(doc, attrs);
260
811
  }
261
- /* Render to HTML with IAL attributes injected */
262
812
  char *html = apex_render_html_with_attributes(doc, cmark_opts);
263
813
  if (html) {
264
814
  /* Write opening tag (without markdown attribute) */
@@ -334,11 +884,10 @@ char *apex_process_html_markdown(const char *text, void *img_attrs) {
334
884
  opening_tag[tag_written] = '\0';
335
885
  }
336
886
 
337
- if (tag_written < remaining) {
338
- memcpy(write_pos, opening_tag, tag_written);
339
- write_pos += tag_written;
340
- remaining -= tag_written;
341
- }
887
+ ENSURE_OUTPUT_SPACE(tag_written);
888
+ memcpy(write_pos, opening_tag, tag_written);
889
+ write_pos += tag_written;
890
+ remaining -= tag_written;
342
891
 
343
892
  /* Write parsed HTML (trim outer <p> tags if inline) */
344
893
  char *html_content = html;
@@ -353,11 +902,10 @@ char *apex_process_html_markdown(const char *text, void *img_attrs) {
353
902
  html_content[html_len] = '\0';
354
903
  }
355
904
 
356
- if (html_len < remaining) {
357
- memcpy(write_pos, html_content, html_len);
358
- write_pos += html_len;
359
- remaining -= html_len;
360
- }
905
+ ENSURE_OUTPUT_SPACE(html_len);
906
+ memcpy(write_pos, html_content, html_len);
907
+ write_pos += html_len;
908
+ remaining -= html_len;
361
909
 
362
910
  free(html);
363
911
  }
@@ -403,6 +951,11 @@ char *apex_process_html_markdown(const char *text, void *img_attrs) {
403
951
  }
404
952
 
405
953
  *write_pos = '\0';
954
+ #undef ENSURE_OUTPUT_SPACE
406
955
  return output;
407
956
  }
408
957
 
958
+ char *apex_process_html_markdown(const char *text, void *img_attrs) {
959
+ return apex_process_html_markdown_impl(text, img_attrs, text);
960
+ }
961
+