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
@@ -43,9 +43,11 @@ void test_cmark_init_callback(void);
43
43
  void test_cmark_callback(void);
44
44
  void test_custom_plugins(void);
45
45
  void test_processor_modes(void);
46
+ void test_quarto_mode(void);
46
47
  void test_multimarkdown_image_attributes(void);
47
48
  void test_file_includes(void);
48
49
  void test_ial(void);
50
+ void test_bear_image_attributes(void);
49
51
  void test_bracketed_spans(void);
50
52
  void test_definition_lists(void);
51
53
  void test_advanced_tables(void);
@@ -58,6 +60,7 @@ void test_callouts(void);
58
60
  void test_blockquote_lists(void);
59
61
  void test_toc(void);
60
62
  void test_terminal_output(void);
63
+ void test_rtf_output(void);
61
64
  void test_html_markdown_attributes(void);
62
65
  void test_fenced_divs(void);
63
66
  void test_sup_sub(void);
@@ -68,6 +71,7 @@ void test_mmd6_features(void);
68
71
  void test_emoji(void);
69
72
  void test_special_markers(void);
70
73
  void test_inline_tables(void);
74
+ void test_grid_tables(void);
71
75
  void test_insert_syntax(void);
72
76
  void test_advanced_footnotes(void);
73
77
  void test_standalone_output(void);
@@ -81,6 +85,7 @@ void test_aria_labels(void);
81
85
  void test_marked_integration_features(void);
82
86
  void test_plugins_integration(void);
83
87
  void test_ast_json_parser(void);
88
+ void test_escaping_repro(void);
84
89
 
85
90
  /**
86
91
  * Test suite registry
@@ -113,8 +118,10 @@ static test_suite suites[] = {
113
118
  { "cmark_callback", test_cmark_callback },
114
119
  { "multimarkdown_image_attributes",test_multimarkdown_image_attributes },
115
120
  { "processor_modes", test_processor_modes },
121
+ { "quarto_mode", test_quarto_mode },
116
122
  { "file_includes", test_file_includes },
117
123
  { "ial", test_ial },
124
+ { "bear_image_attributes", test_bear_image_attributes },
118
125
  { "bracketed_spans", test_bracketed_spans },
119
126
  { "definition_lists", test_definition_lists },
120
127
  { "advanced_tables", test_advanced_tables },
@@ -127,6 +134,7 @@ static test_suite suites[] = {
127
134
  { "blockquote_lists", test_blockquote_lists },
128
135
  { "toc", test_toc },
129
136
  { "terminal_output", test_terminal_output },
137
+ { "rtf_output", test_rtf_output },
130
138
  { "html_markdown_attributes", test_html_markdown_attributes },
131
139
  { "fenced_divs", test_fenced_divs },
132
140
  { "sup_sub", test_sup_sub },
@@ -137,6 +145,7 @@ static test_suite suites[] = {
137
145
  { "emoji", test_emoji },
138
146
  { "special_markers", test_special_markers },
139
147
  { "inline_tables", test_inline_tables },
148
+ { "grid_tables", test_grid_tables },
140
149
  { "insert_syntax", test_insert_syntax },
141
150
  { "advanced_footnotes", test_advanced_footnotes },
142
151
  { "standalone_output", test_standalone_output },
@@ -153,6 +162,8 @@ static test_suite suites[] = {
153
162
  { "plugins_integration", test_plugins_integration },
154
163
  { "plugins_custom", test_custom_plugins },
155
164
  { "ast_json", test_ast_json_parser },
165
+ { "escaping_repro", test_escaping_repro },
166
+ { "escaping", test_escaping_repro },
156
167
  };
157
168
 
158
169
  static const size_t suite_count = sizeof(suites) / sizeof(suites[0]);
@@ -7,6 +7,7 @@
7
7
  #include <string.h>
8
8
  #include <stdlib.h>
9
9
  #include <stdbool.h>
10
+ #include <stdio.h>
10
11
 
11
12
  void test_advanced_tables(void) {
12
13
  int suite_failures = suite_start();
@@ -37,6 +38,120 @@ void test_advanced_tables(void) {
37
38
  assert_contains(html, "Table Caption After", "Caption text after is present");
38
39
  apex_free_string(html);
39
40
 
41
+ /* Regression: footnote definitions must not be interpreted as table captions.
42
+ * Repro from issue #20: inline table followed by [^1]: ...
43
+ */
44
+ apex_options inline_table_opts = apex_options_default();
45
+ inline_table_opts.enable_marked_extensions = true;
46
+ const char *inline_table_footnote_caption_regression =
47
+ "Lorem ipsum dolor sit amet[^1], fabulas propriae signiferumque in ius.\n\n"
48
+ "<<[Table.csv{;}]\n\n"
49
+ "[^1]: Lorem is a good guy\n";
50
+ html = apex_markdown_to_html(inline_table_footnote_caption_regression,
51
+ strlen(inline_table_footnote_caption_regression),
52
+ &inline_table_opts);
53
+ assert_not_contains(html, "<figcaption>^1</figcaption>",
54
+ "Footnote definition is not treated as table caption");
55
+ apex_free_string(html);
56
+
57
+ const char *inline_table_linkdef_caption_regression =
58
+ "Lorem ipsum dolor sit amet [link-ref].\n\n"
59
+ "<<[Table.csv{;}]\n\n"
60
+ "[link-ref]: https://example.com\n";
61
+ html = apex_markdown_to_html(inline_table_linkdef_caption_regression,
62
+ strlen(inline_table_linkdef_caption_regression),
63
+ &inline_table_opts);
64
+ assert_not_contains(html, "<figcaption>link-ref</figcaption>",
65
+ "Link definition is not treated as table caption");
66
+ apex_free_string(html);
67
+
68
+ const char *gfm_table_footnote_caption_regression =
69
+ "Lorem ipsum dolor sit amet[^1], fabulas propriae signiferumque in ius.\n\n"
70
+ "| Lorem | Dolor | Sit |\n"
71
+ "| ------------- | ------- | ------- |\n"
72
+ "| Amet | Fabulas | Propiae |\n"
73
+ "| Signiferumque | In | Ius |\n\n"
74
+ "[^1]: Lorem is a good guy\n";
75
+ html = apex_markdown_to_html(gfm_table_footnote_caption_regression,
76
+ strlen(gfm_table_footnote_caption_regression),
77
+ &opts);
78
+ assert_contains(html, "<table", "GFM table renders in footnote-caption regression");
79
+ assert_not_contains(html, "<figcaption>^1</figcaption>",
80
+ "Footnote definition after GFM table is not treated as caption");
81
+ apex_free_string(html);
82
+
83
+ /* Regression (#22): caption from inclusion-table section must not leak to nearby tables
84
+ * that do not define their own caption.
85
+ */
86
+ const char *inclusion_caption_leak_regression =
87
+ "**First table**\n\n"
88
+ "| Lorem | Dolor | Sit |\n"
89
+ "| ------------- | ------- | ------- |\n"
90
+ "| Amet | Fabulas | Propiae |\n"
91
+ "| Signiferumque | In | Ius |\n"
92
+ ": My little table caption\n\n"
93
+ "**Second table**\n\n"
94
+ "| Lorem | Dolor | Sit |\n"
95
+ "| ------------- | ------- | ------- |\n"
96
+ "| Amet | Fabulas | Propiae |\n"
97
+ "| Signiferumque | In | Ius |\n\n"
98
+ "**Inclusion Table**\n\n"
99
+ "<<[Table.csv]\n"
100
+ ": Caption for inclusion table\n\n"
101
+ "**Third table**\n\n"
102
+ "| Lorem | Dolor | Sit |\n"
103
+ "| ------------- | ------- | ------- |\n"
104
+ "| Amet | Fabulas | Propiae |\n"
105
+ "| Signiferumque | In | Ius |\n";
106
+ html = apex_markdown_to_html(inclusion_caption_leak_regression,
107
+ strlen(inclusion_caption_leak_regression),
108
+ &inline_table_opts);
109
+ assert_contains(html, "My little table caption",
110
+ "Own caption remains attached to first table");
111
+ assert_not_contains(html, "data-caption=\"Caption for inclusion table\"",
112
+ "Inclusion-table caption does not leak into other table attributes");
113
+ assert_not_contains(html, "<figcaption>Caption for inclusion table</figcaption>",
114
+ "Inclusion-table caption does not leak into other table figure captions");
115
+ apex_free_string(html);
116
+
117
+ /* Regression (#20 comment): <<[file] include must not treat following
118
+ * definition lines as include addresses or table captions.
119
+ */
120
+ const char *include_csv_path = "/tmp/apex_issue20_include_table.csv";
121
+ FILE *include_csv = fopen(include_csv_path, "w");
122
+ if (include_csv) {
123
+ fputs("H1,H2\nA,B\n", include_csv);
124
+ fclose(include_csv);
125
+ }
126
+
127
+ char include_table_footnote_def_regression[512];
128
+ snprintf(include_table_footnote_def_regression,
129
+ sizeof(include_table_footnote_def_regression),
130
+ "<<[%s]\n\n[^1]: Some Footnote Text\n",
131
+ include_csv_path);
132
+ html = apex_markdown_to_html(include_table_footnote_def_regression,
133
+ strlen(include_table_footnote_def_regression),
134
+ &inline_table_opts);
135
+ assert_contains(html, "<table", "Include-table with real CSV renders");
136
+ assert_not_contains(html, "<figcaption>Some Footnote Text</figcaption>",
137
+ "Footnote definition after <<[file] is not treated as caption");
138
+ apex_free_string(html);
139
+
140
+ char include_table_link_def_regression[512];
141
+ snprintf(include_table_link_def_regression,
142
+ sizeof(include_table_link_def_regression),
143
+ "<<[%s]\n\n[link-ref]: https://example.com\n",
144
+ include_csv_path);
145
+ html = apex_markdown_to_html(include_table_link_def_regression,
146
+ strlen(include_table_link_def_regression),
147
+ &inline_table_opts);
148
+ assert_contains(html, "<table", "Include-table with link definition renders");
149
+ assert_not_contains(html, "<figcaption>https://example.com</figcaption>",
150
+ "Link definition after <<[file] is not treated as caption");
151
+ apex_free_string(html);
152
+
153
+ remove(include_csv_path);
154
+
40
155
  /* Test rowspan with ^^ */
41
156
  const char *rowspan_table = "| H1 | H2 |\n|----|----|"
42
157
  "\n| A | B |"
@@ -98,6 +213,35 @@ void test_advanced_tables(void) {
98
213
  assert_not_contains(html, "colspan", "Empty cells table: no colspan attribute (empty cells don't create colspan)");
99
214
  apex_free_string(html);
100
215
 
216
+ /* Blank cells use spaced pipes (| |); colspan only for consecutive pipes (||).
217
+ * A later full-width colspan row must not steal blanks from earlier indent rows. */
218
+ const char *blank_then_colspan =
219
+ "|:-|:-|:-|:-|:-|\n"
220
+ "|a|b|c|d|e|\n"
221
+ "| | |x|y|z|\n"
222
+ "| | |p|q|r|\n"
223
+ "|full|||||\n";
224
+ html = apex_markdown_to_html(blank_then_colspan, strlen(blank_then_colspan), &opts);
225
+ assert_contains(html, "<td colspan=\"5\">full</td>", "Blank then colspan: full-width colspan on last row");
226
+ assert_contains(html, ">x</td>", "Blank then colspan: indent cell x present");
227
+ assert_contains(html, ">p</td>", "Blank then colspan: indent cell p present");
228
+ /* First indent row should keep two leading blank cells, not a colspan on an empty cell */
229
+ assert_not_contains(html, "<td colspan=\"5\"></td>", "Blank then colspan: no empty-cell colspan on indent rows");
230
+ assert_not_contains(html, "<th colspan=\"5\"></th>", "Blank then colspan: no empty th colspan on indent rows");
231
+ apex_free_string(html);
232
+
233
+ /* Spaced empty cell between content stays a blank column, not a span */
234
+ const char *spaced_blank_between =
235
+ "| H1 | H2 | H3 |\n"
236
+ "|----|----|----|\n"
237
+ "| A | | B |\n";
238
+ html = apex_markdown_to_html(spaced_blank_between, strlen(spaced_blank_between), &opts);
239
+ assert_contains(html, "<td>A</td>", "Spaced blank between: A present");
240
+ assert_contains(html, "<td></td>", "Spaced blank between: blank cell present");
241
+ assert_contains(html, "<td>B</td>", "Spaced blank between: B present");
242
+ assert_not_contains(html, "colspan", "Spaced blank between: no colspan from | |");
243
+ apex_free_string(html);
244
+
101
245
  /* Test dash-only separator row removal (rows containing only — should be removed). */
102
246
  const char *dash_row =
103
247
  "| H1 | H2 |\n"
@@ -389,6 +533,43 @@ void test_relaxed_tables(void) {
389
533
  }
390
534
  apex_free_string(html);
391
535
 
536
+ /* Pipes inside fenced code blocks must not trigger relaxed table separators */
537
+ const char *fenced_code_with_pipes =
538
+ "```bash\n"
539
+ "update_widgets() {\n"
540
+ " echo foo | sed 's/x/y/' | awk '{print $1}'\n"
541
+ "}\n"
542
+ "```";
543
+ html = apex_markdown_to_html(fenced_code_with_pipes, strlen(fenced_code_with_pipes), &opts);
544
+ assert_contains(html, "<code", "Fenced code block with pipes renders as code");
545
+ assert_contains(html, "echo foo | sed", "Code block content with pipes preserved");
546
+ if (strstr(html, "---|---|---|") == NULL) {
547
+ test_result(true, "No relaxed-table separator injected inside fenced code");
548
+ } else {
549
+ test_result(false, "Relaxed-table separator was injected inside fenced code");
550
+ }
551
+ apex_free_string(html);
552
+
553
+ /* Mermaid edge labels |Yes| / |No| must not get a GFM separator injected */
554
+ {
555
+ apex_options unified = apex_options_default();
556
+ unified.mode = APEX_MODE_UNIFIED;
557
+ unified.relaxed_tables = true;
558
+ unified.enable_tables = true;
559
+ const char *mermaid_pipes =
560
+ "```mermaid\n"
561
+ "graph TD\n"
562
+ " A -->|Yes| B\n"
563
+ " A -->|No| C\n"
564
+ "```\n";
565
+ html = apex_markdown_to_html(mermaid_pipes, strlen(mermaid_pipes), &unified);
566
+ assert_contains(html, "lang=\"mermaid\"", "Mermaid fence keeps language");
567
+ assert_contains(html, "|Yes|", "Mermaid Yes edge label preserved");
568
+ assert_contains(html, "|No|", "Mermaid No edge label preserved");
569
+ assert_not_contains(html, "---|---|---", "No relaxed-table separator inside Mermaid fence");
570
+ apex_free_string(html);
571
+ }
572
+
392
573
  bool had_failures = suite_end(suite_failures);
393
574
  print_suite_title("Relaxed Tables Tests", had_failures, false);
394
575
  }
@@ -873,6 +1054,245 @@ void test_inline_tables(void) {
873
1054
  print_suite_title("Inline Tables Tests", had_failures, false);
874
1055
  }
875
1056
 
1057
+ void test_grid_tables(void) {
1058
+ int suite_failures = suite_start();
1059
+ print_suite_title("Grid Tables Tests", false, true);
1060
+
1061
+ apex_options opts = apex_options_default();
1062
+ opts.enable_tables = true;
1063
+ opts.enable_grid_tables = true;
1064
+ opts.relaxed_tables = false;
1065
+ char *html;
1066
+
1067
+ /* Test basic grid table */
1068
+ const char *basic_grid = "+---+---+\n"
1069
+ "| H1 | H2 |\n"
1070
+ "+===+===+\n"
1071
+ "| C1 | C2 |\n"
1072
+ "+---+---+";
1073
+ html = apex_markdown_to_html(basic_grid, strlen(basic_grid), &opts);
1074
+ assert_contains(html, "<table", "Basic grid table renders");
1075
+ assert_contains(html, "<th>H1</th>", "Grid table header");
1076
+ assert_contains(html, "<td>C1</td>", "Grid table cell");
1077
+ apex_free_string(html);
1078
+
1079
+ /* Test grid table with header separator */
1080
+ const char *header_sep_grid = "+---+---+\n"
1081
+ "| H1 | H2 |\n"
1082
+ "+===+===+\n"
1083
+ "| C1 | C2 |\n"
1084
+ "+---+---+";
1085
+ html = apex_markdown_to_html(header_sep_grid, strlen(header_sep_grid), &opts);
1086
+ assert_contains(html, "<table", "Grid table with header separator renders");
1087
+ assert_contains(html, "<th>H1</th>", "Header separator table has header");
1088
+ apex_free_string(html);
1089
+
1090
+ /* Test grid table with alignment */
1091
+ const char *aligned_grid = "+:---+---+---:+\n"
1092
+ "| C | L | R |\n"
1093
+ "+===+===+===+\n"
1094
+ "| A | B | C |\n"
1095
+ "+---+---+---+";
1096
+ html = apex_markdown_to_html(aligned_grid, strlen(aligned_grid), &opts);
1097
+ assert_contains(html, "<table", "Aligned grid table renders");
1098
+ apex_free_string(html);
1099
+
1100
+ /* Test grid table with footer separator */
1101
+ const char *footer_grid = "+---+---+\n"
1102
+ "| H1 | H2 |\n"
1103
+ "+---+---+\n"
1104
+ "| C1 | C2 |\n"
1105
+ "+===+===+\n"
1106
+ "| F1 | F2 |\n"
1107
+ "+---+---+";
1108
+ html = apex_markdown_to_html(footer_grid, strlen(footer_grid), &opts);
1109
+ assert_contains(html, "<table", "Grid table with footer renders");
1110
+ apex_free_string(html);
1111
+
1112
+ /* Test multi-line cells in grid table */
1113
+ const char *multiline_grid = "+---+---+\n"
1114
+ "| H1 | H2 |\n"
1115
+ "+===+===+\n"
1116
+ "| Line 1 | Cell 2 |\n"
1117
+ "| Line 2 | |\n"
1118
+ "+---+---+";
1119
+ html = apex_markdown_to_html(multiline_grid, strlen(multiline_grid), &opts);
1120
+ assert_contains(html, "<table", "Multi-line grid table renders");
1121
+ assert_contains(html, "Line 1", "Multi-line cell content preserved");
1122
+ assert_contains(html, "Line 2", "Multi-line cell second line preserved");
1123
+ apex_free_string(html);
1124
+
1125
+ /* Test grid table with caption (before) */
1126
+ const char *caption_before = "[Grid Table Caption]\n\n"
1127
+ "+---+---+\n"
1128
+ "| H1 | H2 |\n"
1129
+ "+===+===+\n"
1130
+ "| C1 | C2 |\n"
1131
+ "+---+---+";
1132
+ html = apex_markdown_to_html(caption_before, strlen(caption_before), &opts);
1133
+ assert_contains(html, "<table", "Grid table with caption before renders");
1134
+ assert_contains(html, "<figure", "Grid table with caption wrapped in figure");
1135
+ assert_contains(html, "Grid Table Caption", "Caption text present");
1136
+ apex_free_string(html);
1137
+
1138
+ /* Test grid table with caption (after) */
1139
+ const char *caption_after = "+---+---+\n"
1140
+ "| H1 | H2 |\n"
1141
+ "+===+===+\n"
1142
+ "| C1 | C2 |\n"
1143
+ "+---+---+\n\n"
1144
+ "[Grid Table Caption After]";
1145
+ html = apex_markdown_to_html(caption_after, strlen(caption_after), &opts);
1146
+ assert_contains(html, "<table", "Grid table with caption after renders");
1147
+ assert_contains(html, "Grid Table Caption After", "Caption text after present");
1148
+ apex_free_string(html);
1149
+
1150
+ /* Test grid table in code block (should not be parsed) */
1151
+ const char *code_block_grid = "```\n"
1152
+ "+---+---+\n"
1153
+ "| H1 | H2 |\n"
1154
+ "+===+===+\n"
1155
+ "| C1 | C2 |\n"
1156
+ "+---+---+\n"
1157
+ "```";
1158
+ html = apex_markdown_to_html(code_block_grid, strlen(code_block_grid), &opts);
1159
+ assert_not_contains(html, "<table", "Grid table in code block not parsed");
1160
+ assert_contains(html, "<code", "Code block preserved");
1161
+ apex_free_string(html);
1162
+
1163
+ /* Test mixed grid and pipe tables */
1164
+ const char *mixed_tables = "+---+---+\n"
1165
+ "| G1 | G2 |\n"
1166
+ "+===+===+\n"
1167
+ "| GC1 | GC2 |\n"
1168
+ "+---+---+\n\n"
1169
+ "| P1 | P2 |\n"
1170
+ "|----|----|\n"
1171
+ "| PC1 | PC2 |";
1172
+ html = apex_markdown_to_html(mixed_tables, strlen(mixed_tables), &opts);
1173
+ assert_contains(html, "<table", "Mixed tables render");
1174
+ assert_contains(html, "G1", "Grid table content present");
1175
+ assert_contains(html, "P1", "Pipe table content present");
1176
+ apex_free_string(html);
1177
+
1178
+ /* Test grid table disabled */
1179
+ opts.enable_grid_tables = false;
1180
+ html = apex_markdown_to_html(basic_grid, strlen(basic_grid), &opts);
1181
+ apex_free_string(html);
1182
+
1183
+ /* Complex colspan row spanning full table width (no closing table separator) */
1184
+ opts.enable_grid_tables = true;
1185
+ opts.enable_tables = true;
1186
+ const char *colspan_grid = "+---+---+\n"
1187
+ "| H1 | H2 |\n"
1188
+ "+===+===+\n"
1189
+ "| C1 | C2 |\n"
1190
+ "+---+---+\n"
1191
+ "| Spans both columns here |\n";
1192
+ html = apex_markdown_to_html(colspan_grid, strlen(colspan_grid), &opts);
1193
+ assert_contains(html, "colspan=\"2\"", "Colspan row spans both columns");
1194
+ assert_contains(html, "Spans both columns", "Colspan cell content preserved");
1195
+ apex_free_string(html);
1196
+
1197
+ /* Nested grid inside colspan cell stays in one cell */
1198
+ const char *nested_in_colspan = "+---+---+\n"
1199
+ "| A | B |\n"
1200
+ "+===+===+\n"
1201
+ "| Wide cell |\n"
1202
+ "+---+---+---+\n"
1203
+ "| n1 | n2 | n3 |\n"
1204
+ "+---+---+---+";
1205
+ html = apex_markdown_to_html(nested_in_colspan, strlen(nested_in_colspan), &opts);
1206
+ assert_contains(html, "colspan=\"2\"", "Nested grid parent has colspan");
1207
+ assert_contains(html, "Wide cell", "Wide cell text preserved");
1208
+ assert_not_contains(html, "<tr>\n<td>n1</td>\n<td>n2</td>", "Nested grid not separate top-level row");
1209
+ apex_free_string(html);
1210
+
1211
+ /* Fixture table 1: colspan + nested grid stays inside the table, not a stray paragraph */
1212
+ const char *fixture_colspan_nested = "+-------------------+-------------------+\n"
1213
+ "| Grid Tables | Are Beautiful |\n"
1214
+ "+===================+===================+\n"
1215
+ "| Easy to read | In code and docs |\n"
1216
+ "+-------------------+-------------------+\n"
1217
+ "| Exceptionally flexible and powerful |\n"
1218
+ "+-------+-------+-------+-------+-------+\n"
1219
+ "| Col 1 | Col 2 | Col 3 | Col 4 | Col 5 |\n"
1220
+ "+-------+-------+-------+-------+-------+\n";
1221
+ html = apex_markdown_to_html(fixture_colspan_nested, strlen(fixture_colspan_nested), &opts);
1222
+ assert_contains(html, "colspan=\"2\"", "Fixture colspan row in table");
1223
+ assert_contains(html, "Exceptionally flexible and powerful", "Fixture colspan text in output");
1224
+ assert_not_contains(html, "</table>\n<p>|", "Colspan row not leaked as paragraph after table");
1225
+ assert_contains(html, "Col 1", "Nested grid Col 1 inside colspan cell");
1226
+ assert_contains(html, "Col 5", "Nested grid Col 5 inside colspan cell");
1227
+ apex_free_string(html);
1228
+
1229
+ /* Partial separator table (fixture table 2 pattern) */
1230
+ const char *partial_grid = "+---------------------+----------+\n"
1231
+ "| Property | Earth |\n"
1232
+ "+=============+=======+==========+\n"
1233
+ "| | min | -89.2 °C |\n"
1234
+ "| Temperature +-------+----------+\n"
1235
+ "| 1961-1990 | mean | 14 °C |\n"
1236
+ "| +-------+----------+\n"
1237
+ "| | min | 56.7 °C |\n"
1238
+ "+-------------+-------+----------+\n";
1239
+ html = apex_markdown_to_html(partial_grid, strlen(partial_grid), &opts);
1240
+ assert_contains(html, "colspan=\"2\"", "Partial table Property spans two columns");
1241
+ assert_contains(html, "rowspan=\"3\"", "Partial table Temperature rowspan");
1242
+ assert_contains(html, "14 °C", "Partial table mean temperature");
1243
+ apex_free_string(html);
1244
+
1245
+ /* Multiline list cells */
1246
+ const char *list_cells = "+---+---+---+\n"
1247
+ "| F | P | Notes |\n"
1248
+ "+===+===+===+\n"
1249
+ "| Ban | $1 | - one |\n"
1250
+ "| | | - two |\n"
1251
+ "+---+---+---+";
1252
+ html = apex_markdown_to_html(list_cells, strlen(list_cells), &opts);
1253
+ assert_contains(html, "<ul>", "List cell renders as ul");
1254
+ assert_contains(html, "<li>one</li>", "List item one in cell");
1255
+ assert_contains(html, "<li>two</li>", "List item two in cell");
1256
+ assert_contains(html, "<table", "List cells table renders");
1257
+ apex_free_string(html);
1258
+
1259
+ /* Plus-prefixed prose lines are not mistaken for grid tables */
1260
+ const char *plus_prose = "+ Item one\n+ Item two\n\nParagraph after.";
1261
+ html = apex_markdown_to_html(plus_prose, strlen(plus_prose), &opts);
1262
+ assert_contains(html, "Item one", "Plus prose lines preserved as list items");
1263
+ assert_contains(html, "Paragraph after", "Text after plus lines preserved");
1264
+ assert_not_contains(html, "<table", "Plus prose is not converted to a table");
1265
+ apex_free_string(html);
1266
+
1267
+ /* Block with +---+ lines but no valid grid content is preserved on failure */
1268
+ const char *invalid_grid = "+---+\nThis line has no pipes\n+---+";
1269
+ html = apex_markdown_to_html(invalid_grid, strlen(invalid_grid), &opts);
1270
+ assert_contains(html, "This line has no pipes", "Invalid grid block content preserved");
1271
+ apex_free_string(html);
1272
+
1273
+ /* Grid tables disabled by default in unified mode */
1274
+ apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
1275
+ html = apex_markdown_to_html(basic_grid, strlen(basic_grid), &unified_opts);
1276
+ assert_not_contains(html, "<th>H1</th>", "Grid tables off by default in unified mode");
1277
+ apex_free_string(html);
1278
+
1279
+ /* Pandoc-style simple grid (manual example pattern) */
1280
+ const char *pandoc_simple = "+---------------+---------------+\n"
1281
+ "| Fruit | Price |\n"
1282
+ "+:==============+==============:+\n"
1283
+ "| Bananas | $1.34 |\n"
1284
+ "| Oranges | $2.10 |\n"
1285
+ "+---------------+---------------+\n";
1286
+ html = apex_markdown_to_html(pandoc_simple, strlen(pandoc_simple), &opts);
1287
+ assert_contains(html, "Bananas", "Pandoc-style aligned grid renders");
1288
+ assert_contains(html, "$1.34", "Pandoc-style grid cell content");
1289
+ assert_contains(html, "<table", "Pandoc-style grid produces table");
1290
+ apex_free_string(html);
1291
+
1292
+ bool had_failures = suite_end(suite_failures);
1293
+ print_suite_title("Grid Tables Tests", had_failures, false);
1294
+ }
1295
+
876
1296
  /**
877
1297
  * Test advanced footnotes
878
1298
  */
@@ -1,4 +1,4 @@
1
- cmake_minimum_required(VERSION 3.0)
1
+ cmake_minimum_required(VERSION 3.5)
2
2
  project(cmark-gfm)
3
3
 
4
4
  set(PROJECT_VERSION_MAJOR 0)
@@ -267,7 +267,7 @@ static cmark_node *url_match(cmark_parser *parser, cmark_node *parent,
267
267
  cmark_node *text = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
268
268
  text->as.literal = url;
269
269
  cmark_node_append_child(node, text);
270
-
270
+
271
271
  node->start_line = text->start_line = node->end_line = text->end_line = cmark_inline_parser_get_line(inline_parser);
272
272
 
273
273
  node->start_column = text->start_column = max_rewind - rewind;
@@ -318,6 +318,64 @@ static bool validate_protocol(const char protocol[], uint8_t *data, size_t rewin
318
318
  return !cmark_isalnum(prev_char);
319
319
  }
320
320
 
321
+ static bool inside_html_tag(const uint8_t *data, size_t at_pos) {
322
+ bool in_tag = false;
323
+ uint8_t quote = 0;
324
+ size_t i;
325
+
326
+ for (i = 0; i < at_pos; ++i) {
327
+ uint8_t c = data[i];
328
+
329
+ if (!in_tag) {
330
+ if (c == '<')
331
+ in_tag = true;
332
+ continue;
333
+ }
334
+
335
+ if (quote != 0) {
336
+ if (c == quote)
337
+ quote = 0;
338
+ continue;
339
+ }
340
+
341
+ if (c == '"' || c == '\'') {
342
+ quote = c;
343
+ continue;
344
+ }
345
+
346
+ if (c == '>')
347
+ in_tag = false;
348
+ }
349
+
350
+ return in_tag;
351
+ }
352
+
353
+ static bool is_image_density_suffix(const uint8_t *data, size_t size) {
354
+ size_t i = 0;
355
+
356
+ if (size < 4)
357
+ return false;
358
+
359
+ while (i < size && cmark_isdigit(data[i]))
360
+ i++;
361
+
362
+ if (i == 0 || i >= size || data[i] != 'x')
363
+ return false;
364
+ i++;
365
+
366
+ if (i >= size || data[i] != '.')
367
+ return false;
368
+ i++;
369
+
370
+ if (i >= size || !cmark_isalnum(data[i]))
371
+ return false;
372
+
373
+ while (i < size && cmark_isalnum(data[i]))
374
+ i++;
375
+
376
+ return i == size;
377
+ }
378
+
321
379
  static void postprocess_text(cmark_parser *parser, cmark_node *text) {
322
380
  size_t start = 0;
323
381
  size_t offset = 0;
@@ -350,6 +408,11 @@ static void postprocess_text(cmark_parser *parser, cmark_node *text) {
350
408
 
351
409
  max_rewind = at - (data + start + offset);
352
410
 
411
+ if (inside_html_tag(data + start + offset, max_rewind)) {
412
+ offset += max_rewind + 1;
413
+ continue;
414
+ }
415
+
353
416
  found_at:
354
417
  for (rewind = 0; rewind < max_rewind; ++rewind) {
355
418
  uint8_t c = data[start + offset + max_rewind - rewind - 1];
@@ -381,6 +444,14 @@ found_at:
381
444
  continue;
382
445
  }
383
446
 
447
+ /* Require that the character immediately before @ is alphanumeric.
448
+ * This prevents matching @ in URLs like https://example.com/@user */
449
+ uint8_t char_before_at = data[start + offset + max_rewind - 1];
450
+ if (!cmark_isalnum(char_before_at)) {
451
+ offset += max_rewind + 1;
452
+ continue;
453
+ }
454
+
384
455
  assert(data[start + offset + max_rewind] == '@');
385
456
  for (link_end = 1; link_end < remaining - offset - max_rewind; ++link_end) {
386
457
  uint8_t c = data[start + offset + max_rewind + link_end];
@@ -409,6 +480,11 @@ found_at:
409
480
  continue;
410
481
  }
411
482
 
483
+ if (is_image_density_suffix(data + start + offset + max_rewind + 1, link_end - 1)) {
484
+ offset += max_rewind + link_end;
485
+ continue;
486
+ }
487
+
412
488
  link_end = autolink_delim(data + start + offset + max_rewind, link_end);
413
489
 
414
490
  if (link_end == 0) {