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
@@ -5,6 +5,7 @@
5
5
 
6
6
  #include "includes.h"
7
7
  #include "metadata.h"
8
+ #include "../plugins.h"
8
9
  #include <stdio.h>
9
10
  #include <stdlib.h>
10
11
  #include <string.h>
@@ -1236,6 +1237,242 @@ static bool parse_embedded_delimiter_override(char *filepath, char *delimiter_ou
1236
1237
  return true;
1237
1238
  }
1238
1239
 
1240
+ static char *trim_copy(const char *s, size_t len) {
1241
+ if (!s) return NULL;
1242
+ while (len > 0 && isspace((unsigned char)*s)) {
1243
+ s++;
1244
+ len--;
1245
+ }
1246
+ while (len > 0 && isspace((unsigned char)s[len - 1])) {
1247
+ len--;
1248
+ }
1249
+ char *out = malloc(len + 1);
1250
+ if (!out) return NULL;
1251
+ memcpy(out, s, len);
1252
+ out[len] = '\0';
1253
+ return out;
1254
+ }
1255
+
1256
+ static void split_section_fragment(char *filepath, char **section_out) {
1257
+ if (section_out) *section_out = NULL;
1258
+ if (!filepath) return;
1259
+
1260
+ char *hash = strchr(filepath, '#');
1261
+ if (!hash) return;
1262
+
1263
+ *hash = '\0';
1264
+ hash++;
1265
+ while (*hash && isspace((unsigned char)*hash)) hash++;
1266
+ if (!*hash) return;
1267
+
1268
+ if (section_out) {
1269
+ *section_out = strdup(hash);
1270
+ }
1271
+ }
1272
+
1273
+ static bool has_extension(const char *path) {
1274
+ if (!path || !*path) return false;
1275
+ const char *slash = strrchr(path, '/');
1276
+ const char *base = slash ? slash + 1 : path;
1277
+ const char *dot = strrchr(base, '.');
1278
+ return dot && dot[1] != '\0';
1279
+ }
1280
+
1281
+ static char *with_default_extension(const char *path, const char *ext_without_dot) {
1282
+ if (!path || !*path) return NULL;
1283
+ if (has_extension(path)) return strdup(path);
1284
+ if (!ext_without_dot || !*ext_without_dot) return strdup(path);
1285
+
1286
+ size_t len = strlen(path) + strlen(ext_without_dot) + 2;
1287
+ char *out = malloc(len);
1288
+ if (!out) return NULL;
1289
+ snprintf(out, len, "%s.%s", path, ext_without_dot);
1290
+ return out;
1291
+ }
1292
+
1293
+ static const char *next_line_start(const char *line_end) {
1294
+ if (!line_end) return NULL;
1295
+ if (*line_end == '\n') return line_end + 1;
1296
+ if (*line_end == '\0') return line_end;
1297
+ return line_end + 1;
1298
+ }
1299
+
1300
+ static bool parse_setext_underline(const char *line, size_t len, int *level_out) {
1301
+ if (!line || len == 0) return false;
1302
+ size_t i = 0;
1303
+ while (i < len && line[i] == ' ') i++;
1304
+ if (i > 3 || i >= len) return false;
1305
+
1306
+ char marker = 0;
1307
+ int marker_count = 0;
1308
+ for (; i < len; i++) {
1309
+ char c = line[i];
1310
+ if (c == '\r') continue;
1311
+ if (c == '=' || c == '-') {
1312
+ if (marker == 0) marker = c;
1313
+ if (c != marker) return false;
1314
+ marker_count++;
1315
+ continue;
1316
+ }
1317
+ if (!isspace((unsigned char)c)) return false;
1318
+ }
1319
+ if (marker_count == 0 || marker == 0) return false;
1320
+ if (level_out) *level_out = (marker == '=') ? 1 : 2;
1321
+ return true;
1322
+ }
1323
+
1324
+ static char *normalize_heading_text(const char *start, size_t len, bool is_atx) {
1325
+ if (!start) return NULL;
1326
+
1327
+ while (len > 0 && isspace((unsigned char)*start)) {
1328
+ start++;
1329
+ len--;
1330
+ }
1331
+ while (len > 0 && isspace((unsigned char)start[len - 1])) {
1332
+ len--;
1333
+ }
1334
+
1335
+ if (is_atx && len > 0) {
1336
+ size_t t = len;
1337
+ while (t > 0 && start[t - 1] == '#') t--;
1338
+ if (t < len) {
1339
+ size_t u = t;
1340
+ while (u > 0 && isspace((unsigned char)start[u - 1])) u--;
1341
+ if (u < t) len = u;
1342
+ }
1343
+ }
1344
+
1345
+ /* Collapse spaces and lowercase for matching. */
1346
+ char *out = malloc(len + 1);
1347
+ if (!out) return NULL;
1348
+
1349
+ size_t w = 0;
1350
+ bool in_space = false;
1351
+ for (size_t i = 0; i < len; i++) {
1352
+ unsigned char c = (unsigned char)start[i];
1353
+ if (isspace(c)) {
1354
+ if (!in_space) {
1355
+ out[w++] = ' ';
1356
+ in_space = true;
1357
+ }
1358
+ continue;
1359
+ }
1360
+ out[w++] = (char)tolower(c);
1361
+ in_space = false;
1362
+ }
1363
+ while (w > 0 && out[w - 1] == ' ') w--;
1364
+ out[w] = '\0';
1365
+ return out;
1366
+ }
1367
+
1368
+ static bool parse_atx_heading(const char *line_start, const char *line_end,
1369
+ int *level_out, char **normalized_out) {
1370
+ if (normalized_out) *normalized_out = NULL;
1371
+ if (!line_start || !line_end || line_end < line_start) return false;
1372
+
1373
+ const char *p = line_start;
1374
+ int leading_spaces = 0;
1375
+ while (p < line_end && *p == ' ' && leading_spaces < 4) {
1376
+ p++;
1377
+ leading_spaces++;
1378
+ }
1379
+ if (leading_spaces > 3 || p >= line_end || *p != '#') return false;
1380
+
1381
+ int level = 0;
1382
+ while (p < line_end && *p == '#' && level < 6) {
1383
+ p++;
1384
+ level++;
1385
+ }
1386
+ if (level == 0) return false;
1387
+ if (p < line_end && !isspace((unsigned char)*p)) return false;
1388
+
1389
+ while (p < line_end && isspace((unsigned char)*p)) p++;
1390
+ size_t text_len = (size_t)(line_end - p);
1391
+
1392
+ if (level_out) *level_out = level;
1393
+ if (normalized_out) {
1394
+ *normalized_out = normalize_heading_text(p, text_len, true);
1395
+ }
1396
+ return true;
1397
+ }
1398
+
1399
+ static char *extract_markdown_section(const char *content, const char *section_name) {
1400
+ if (!content || !section_name || !*section_name) {
1401
+ return content ? strdup(content) : NULL;
1402
+ }
1403
+
1404
+ char *target = normalize_heading_text(section_name, strlen(section_name), false);
1405
+ if (!target || !*target) {
1406
+ if (target) free(target);
1407
+ return NULL;
1408
+ }
1409
+
1410
+ const char *cursor = content;
1411
+ const char *section_start = NULL;
1412
+ const char *section_end = NULL;
1413
+ int target_level = 0;
1414
+ bool found = false;
1415
+
1416
+ while (*cursor) {
1417
+ const char *line_end = strchr(cursor, '\n');
1418
+ if (!line_end) line_end = cursor + strlen(cursor);
1419
+ const char *next = next_line_start(line_end);
1420
+ const char *next_end = (*next) ? (strchr(next, '\n') ? strchr(next, '\n') : next + strlen(next)) : next;
1421
+
1422
+ int level = 0;
1423
+ char *heading_norm = NULL;
1424
+ bool heading_found = parse_atx_heading(cursor, line_end, &level, &heading_norm);
1425
+ bool is_setext = false;
1426
+
1427
+ if (!heading_found && *next && next > cursor) {
1428
+ int setext_level = 0;
1429
+ size_t cur_len = (size_t)(line_end - cursor);
1430
+ if (cur_len > 0 && parse_setext_underline(next, (size_t)(next_end - next), &setext_level)) {
1431
+ heading_found = true;
1432
+ is_setext = true;
1433
+ level = setext_level;
1434
+ heading_norm = normalize_heading_text(cursor, cur_len, false);
1435
+ }
1436
+ }
1437
+
1438
+ if (heading_found) {
1439
+ if (!found) {
1440
+ if (heading_norm && strcmp(heading_norm, target) == 0) {
1441
+ found = true;
1442
+ target_level = level;
1443
+ section_start = cursor;
1444
+ if (is_setext) {
1445
+ cursor = (*next_end == '\n') ? (next_end + 1) : next_end;
1446
+ free(heading_norm);
1447
+ continue;
1448
+ }
1449
+ }
1450
+ } else {
1451
+ if (level <= target_level) {
1452
+ section_end = cursor;
1453
+ free(heading_norm);
1454
+ break;
1455
+ }
1456
+ }
1457
+ }
1458
+
1459
+ if (heading_norm) free(heading_norm);
1460
+ cursor = (*line_end == '\n') ? (line_end + 1) : line_end;
1461
+ }
1462
+
1463
+ free(target);
1464
+
1465
+ if (!found || !section_start) return NULL;
1466
+ if (!section_end) section_end = content + strlen(content);
1467
+
1468
+ size_t out_len = (size_t)(section_end - section_start);
1469
+ char *out = malloc(out_len + 1);
1470
+ if (!out) return NULL;
1471
+ memcpy(out, section_start, out_len);
1472
+ out[out_len] = '\0';
1473
+ return out;
1474
+ }
1475
+
1239
1476
  /* Find closing "}}" for MMD transclusion, allowing embedded single-brace
1240
1477
  * segments inside filepath (e.g. {{data.csv{;}}}). */
1241
1478
  static const char *find_mmd_transclusion_end(const char *filepath_start) {
@@ -1263,10 +1500,34 @@ static const char *find_mmd_transclusion_end(const char *filepath_start) {
1263
1500
  return NULL;
1264
1501
  }
1265
1502
 
1503
+ static char *run_preparse_plugins_on_text(const char *text,
1504
+ apex_plugin_manager *plugin_manager,
1505
+ const apex_options *options) {
1506
+ if (!text) {
1507
+ return NULL;
1508
+ }
1509
+
1510
+ if (!plugin_manager || !options) {
1511
+ return NULL;
1512
+ }
1513
+
1514
+ char *plugin_text = apex_plugins_run_text_phase(plugin_manager,
1515
+ APEX_PLUGIN_PHASE_PRE_PARSE,
1516
+ text,
1517
+ options);
1518
+ return plugin_text;
1519
+ }
1520
+
1266
1521
  /**
1267
1522
  * Process file includes in text
1268
1523
  */
1269
- char *apex_process_includes(const char *text, const char *base_dir, apex_metadata_item *metadata, int depth) {
1524
+ char *apex_process_includes(const char *text,
1525
+ const char *base_dir,
1526
+ apex_metadata_item *metadata,
1527
+ int depth,
1528
+ const char *default_extension,
1529
+ apex_plugin_manager *plugin_manager,
1530
+ const apex_options *options) {
1270
1531
  if (!text) return NULL;
1271
1532
  if (depth > MAX_INCLUDE_DEPTH) {
1272
1533
  return strdup(text); /* Silently return original text */
@@ -1312,26 +1573,197 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1312
1573
  in_code_span = !in_code_span;
1313
1574
  }
1314
1575
 
1576
+ /* Obsidian embed syntax: ![[file]] or ![[file#Section]] */
1577
+ if (!in_code_span && read_pos[0] == '!' && read_pos[1] == '[' && read_pos[2] == '[') {
1578
+ const char *content_start = read_pos + 3;
1579
+ const char *close = strstr(content_start, "]]");
1580
+ if (close && (close - content_start) > 0 && (close - content_start) < 1024) {
1581
+ char target[1024];
1582
+ size_t target_len = (size_t)(close - content_start);
1583
+ memcpy(target, content_start, target_len);
1584
+ target[target_len] = '\0';
1585
+
1586
+ char *pipe = strchr(target, '|');
1587
+ if (pipe) *pipe = '\0'; /* Ignore alias text for includes */
1588
+
1589
+ percent_decode_inplace(target);
1590
+
1591
+ char *section_name = NULL;
1592
+ split_section_fragment(target, &section_name);
1593
+
1594
+ const char *effective_default_ext = default_extension;
1595
+ while (effective_default_ext && *effective_default_ext == '.') {
1596
+ effective_default_ext++;
1597
+ }
1598
+ if (effective_default_ext && *effective_default_ext == '\0') {
1599
+ effective_default_ext = NULL;
1600
+ }
1601
+
1602
+ bool had_explicit_ext = has_extension(target);
1603
+ char *target_with_ext = with_default_extension(target, effective_default_ext ? effective_default_ext : "md");
1604
+ char *resolved_path = apex_resolve_wildcard(target_with_ext ? target_with_ext : target, effective_base_dir);
1605
+ if (!resolved_path) {
1606
+ resolved_path = resolve_path(target_with_ext ? target_with_ext : target, effective_base_dir);
1607
+ }
1608
+
1609
+ /* If a non-md default extension was configured but didn't resolve,
1610
+ * fall back to .md to keep Obsidian-style embeds practical. */
1611
+ if (!had_explicit_ext &&
1612
+ (!resolved_path || !apex_file_exists(resolved_path)) &&
1613
+ effective_default_ext &&
1614
+ strcasecmp(effective_default_ext, "md") != 0) {
1615
+ char *md_target = with_default_extension(target, "md");
1616
+ char *md_resolved = apex_resolve_wildcard(md_target ? md_target : target, effective_base_dir);
1617
+ if (!md_resolved) {
1618
+ md_resolved = resolve_path(md_target ? md_target : target, effective_base_dir);
1619
+ }
1620
+ if (md_target) free(md_target);
1621
+ if (md_resolved && apex_file_exists(md_resolved)) {
1622
+ if (resolved_path) free(resolved_path);
1623
+ resolved_path = md_resolved;
1624
+ } else if (md_resolved) {
1625
+ free(md_resolved);
1626
+ }
1627
+ }
1628
+
1629
+ if (resolved_path && apex_file_exists(resolved_path)) {
1630
+ apex_file_type_t file_type = apex_detect_file_type(resolved_path);
1631
+ char *content = read_file_contents(resolved_path);
1632
+ if (content) {
1633
+ char *to_insert = NULL;
1634
+ bool free_to_insert = false;
1635
+
1636
+ if (file_type == FILE_TYPE_IMAGE) {
1637
+ size_t buf_size = strlen(target_with_ext ? target_with_ext : target) + 10;
1638
+ to_insert = malloc(buf_size);
1639
+ if (to_insert) snprintf(to_insert, buf_size, "![](%s)\n", target_with_ext ? target_with_ext : target);
1640
+ free_to_insert = true;
1641
+ } else if (file_type == FILE_TYPE_CSV || file_type == FILE_TYPE_TSV) {
1642
+ char *plugin_csv_content = run_preparse_plugins_on_text(content, plugin_manager, options);
1643
+ const char *csv_content = plugin_csv_content ? plugin_csv_content : content;
1644
+ to_insert = apex_csv_to_table_with_delimiter(csv_content, file_type == FILE_TYPE_TSV, '\0');
1645
+ if (plugin_csv_content) free(plugin_csv_content);
1646
+ free_to_insert = true;
1647
+ } else if (file_type == FILE_TYPE_CODE) {
1648
+ char *plugin_code_content = run_preparse_plugins_on_text(content, plugin_manager, options);
1649
+ const char *code_content = plugin_code_content ? plugin_code_content : content;
1650
+ const char *ext = strrchr(target_with_ext ? target_with_ext : target, '.');
1651
+ const char *lang = ext ? ext + 1 : "";
1652
+ size_t buf_size = strlen(code_content) + strlen(lang) + 20;
1653
+ to_insert = malloc(buf_size);
1654
+ if (to_insert) snprintf(to_insert, buf_size, "\n```%s\n%s\n```\n", lang, code_content);
1655
+ if (plugin_code_content) free(plugin_code_content);
1656
+ free_to_insert = true;
1657
+ } else {
1658
+ char *section_content = section_name ? extract_markdown_section(content, section_name) : NULL;
1659
+ char *base_content = section_content ? section_content : strdup(content);
1660
+ char *plugin_base_content = run_preparse_plugins_on_text(base_content ? base_content : content,
1661
+ plugin_manager,
1662
+ options);
1663
+ const char *content_for_processing = plugin_base_content ? plugin_base_content : (base_content ? base_content : content);
1664
+
1665
+ char *file_content_for_metadata = strdup(content_for_processing);
1666
+ apex_metadata_item *file_metadata = NULL;
1667
+ char *file_text_after_metadata = file_content_for_metadata;
1668
+ if (file_content_for_metadata) {
1669
+ file_metadata = apex_extract_metadata(&file_text_after_metadata);
1670
+ }
1671
+
1672
+ char *transclude_base = NULL;
1673
+ if (file_metadata) {
1674
+ transclude_base = get_transclude_base(get_directory(resolved_path), file_metadata);
1675
+ }
1676
+ if (!transclude_base) {
1677
+ transclude_base = get_directory(resolved_path);
1678
+ }
1679
+
1680
+ to_insert = apex_process_includes(content_for_processing,
1681
+ transclude_base,
1682
+ file_metadata,
1683
+ depth + 1,
1684
+ default_extension,
1685
+ plugin_manager,
1686
+ options);
1687
+ free_to_insert = true;
1688
+
1689
+ if (transclude_base) free(transclude_base);
1690
+ if (file_metadata) apex_free_metadata(file_metadata);
1691
+ if (file_content_for_metadata) free(file_content_for_metadata);
1692
+ if (plugin_base_content) free(plugin_base_content);
1693
+ if (base_content) free(base_content);
1694
+ }
1695
+
1696
+ if (to_insert) {
1697
+ size_t insert_len = strlen(to_insert);
1698
+ if (insert_len < remaining) {
1699
+ memcpy(write_pos, to_insert, insert_len);
1700
+ write_pos += insert_len;
1701
+ remaining -= insert_len;
1702
+ }
1703
+ if (free_to_insert) free(to_insert);
1704
+ }
1705
+
1706
+ free(content);
1707
+ read_pos = close + 2;
1708
+ processed_include = true;
1709
+ }
1710
+ }
1711
+
1712
+ if (target_with_ext) free(target_with_ext);
1713
+ if (resolved_path) free(resolved_path);
1714
+ if (section_name) free(section_name);
1715
+ }
1716
+ }
1717
+
1315
1718
  /* Look for iA Writer transclusion /filename (at start of line only) */
1316
1719
  if (!in_code_span && read_pos[0] == '/' && (read_pos == text_to_process || read_pos[-1] == '\n')) {
1317
1720
  const char *filepath_start = read_pos + 1;
1318
- const char *filepath_end = filepath_start;
1319
-
1320
- /* Find end of filename */
1321
- while (*filepath_end && *filepath_end != ' ' && *filepath_end != '\t' &&
1322
- *filepath_end != '\n' && *filepath_end != '\r') {
1323
- filepath_end++;
1721
+ const char *line_end = read_pos;
1722
+ while (*line_end && *line_end != '\n' && *line_end != '\r') {
1723
+ line_end++;
1324
1724
  }
1325
1725
 
1326
- if (filepath_end > filepath_start && (filepath_end - filepath_start) < 1024) {
1726
+ if (line_end > filepath_start && (line_end - filepath_start) < 1024) {
1327
1727
  char filepath[1024];
1328
- size_t filepath_len = filepath_end - filepath_start;
1728
+ size_t filepath_len = (size_t)(line_end - filepath_start);
1329
1729
  char ia_delimiter_override = '\0';
1330
1730
  memcpy(filepath, filepath_start, filepath_len);
1331
1731
  filepath[filepath_len] = '\0';
1732
+
1733
+ /* Trim leading/trailing whitespace for line-based iA include. */
1734
+ char *trimmed = trim_copy(filepath, strlen(filepath));
1735
+ if (!trimmed) {
1736
+ read_pos = line_end;
1737
+ processed_include = true;
1738
+ continue;
1739
+ }
1740
+ strncpy(filepath, trimmed, sizeof(filepath) - 1);
1741
+ filepath[sizeof(filepath) - 1] = '\0';
1742
+ free(trimmed);
1743
+
1744
+ /* Allow trailing delimiter override on iA syntax, e.g. /data.csv {delimiter=;} */
1745
+ char *last_brace = strrchr(filepath, '{');
1746
+ if (last_brace) {
1747
+ const char *after_override = NULL;
1748
+ char parsed = '\0';
1749
+ if (parse_include_delimiter_override(last_brace, &after_override, &parsed) &&
1750
+ after_override && *after_override == '\0') {
1751
+ ia_delimiter_override = parsed;
1752
+ *last_brace = '\0';
1753
+ size_t trim_len = strlen(filepath);
1754
+ while (trim_len > 0 && isspace((unsigned char)filepath[trim_len - 1])) {
1755
+ filepath[trim_len - 1] = '\0';
1756
+ trim_len--;
1757
+ }
1758
+ }
1759
+ }
1760
+
1332
1761
  percent_decode_inplace(filepath);
1333
1762
  parse_embedded_delimiter_override(filepath, &ia_delimiter_override);
1334
1763
 
1764
+ char *section_name = NULL;
1765
+ split_section_fragment(filepath, &section_name);
1766
+
1335
1767
  /* Resolve and check file exists */
1336
1768
  char *resolved_path = resolve_path(filepath, effective_base_dir);
1337
1769
  if (resolved_path && apex_file_exists(resolved_path)) {
@@ -1349,23 +1781,31 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1349
1781
  } else if (file_type == FILE_TYPE_CSV || file_type == FILE_TYPE_TSV) {
1350
1782
  /* CSV/TSV: convert to table */
1351
1783
  char delimiter_override = ia_delimiter_override;
1352
- const char *override_end = filepath_end;
1353
- if (delimiter_override == '\0' &&
1354
- parse_include_delimiter_override(filepath_end, &override_end, &delimiter_override)) {
1355
- filepath_end = override_end;
1356
- }
1357
- to_insert = apex_csv_to_table_with_delimiter(content, file_type == FILE_TYPE_TSV, delimiter_override);
1784
+ char *plugin_csv_content = run_preparse_plugins_on_text(content, plugin_manager, options);
1785
+ const char *csv_content = plugin_csv_content ? plugin_csv_content : content;
1786
+ to_insert = apex_csv_to_table_with_delimiter(csv_content, file_type == FILE_TYPE_TSV, delimiter_override);
1787
+ if (plugin_csv_content) free(plugin_csv_content);
1358
1788
  } else if (file_type == FILE_TYPE_CODE) {
1359
1789
  /* Code: wrap in fenced code block */
1790
+ char *plugin_code_content = run_preparse_plugins_on_text(content, plugin_manager, options);
1791
+ const char *code_content = plugin_code_content ? plugin_code_content : content;
1360
1792
  const char *ext = strrchr(filepath, '.');
1361
1793
  const char *lang = ext ? ext + 1 : "";
1362
- size_t buf_size = strlen(content) + strlen(lang) + 20;
1794
+ size_t buf_size = strlen(code_content) + strlen(lang) + 20;
1363
1795
  to_insert = malloc(buf_size);
1364
- if (to_insert) snprintf(to_insert, buf_size, "\n```%s\n%s\n```\n", lang, content);
1796
+ if (to_insert) snprintf(to_insert, buf_size, "\n```%s\n%s\n```\n", lang, code_content);
1797
+ if (plugin_code_content) free(plugin_code_content);
1365
1798
  } else {
1366
1799
  /* Text/Markdown: process and include */
1800
+ char *section_content = section_name ? extract_markdown_section(content, section_name) : NULL;
1801
+ char *base_text = section_content ? section_content : strdup(content);
1802
+ char *plugin_base_text = run_preparse_plugins_on_text(base_text ? base_text : content,
1803
+ plugin_manager,
1804
+ options);
1805
+ const char *content_for_processing = plugin_base_text ? plugin_base_text : (base_text ? base_text : content);
1806
+
1367
1807
  /* Extract metadata from transcluded file */
1368
- char *file_content_for_metadata = strdup(content);
1808
+ char *file_content_for_metadata = strdup(content_for_processing);
1369
1809
  apex_metadata_item *file_metadata = NULL;
1370
1810
  char *file_text_after_metadata = file_content_for_metadata;
1371
1811
  if (file_content_for_metadata) {
@@ -1381,12 +1821,20 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1381
1821
  transclude_base = get_directory(resolved_path);
1382
1822
  }
1383
1823
 
1384
- to_insert = apex_process_includes(content, transclude_base, file_metadata, depth + 1);
1824
+ to_insert = apex_process_includes(content_for_processing,
1825
+ transclude_base,
1826
+ file_metadata,
1827
+ depth + 1,
1828
+ default_extension,
1829
+ plugin_manager,
1830
+ options);
1385
1831
 
1386
1832
  /* Cleanup */
1387
1833
  if (transclude_base) free(transclude_base);
1388
1834
  if (file_metadata) apex_free_metadata(file_metadata);
1389
1835
  if (file_content_for_metadata) free(file_content_for_metadata);
1836
+ if (plugin_base_text) free(plugin_base_text);
1837
+ if (base_text) free(base_text);
1390
1838
  }
1391
1839
 
1392
1840
  if (to_insert) {
@@ -1401,13 +1849,16 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1401
1849
 
1402
1850
  free(content);
1403
1851
  free(resolved_path);
1404
- read_pos = filepath_end;
1852
+ read_pos = line_end;
1405
1853
  processed_include = true;
1854
+ if (section_name) free(section_name);
1406
1855
  } else {
1407
1856
  free(resolved_path);
1857
+ if (section_name) free(section_name);
1408
1858
  }
1409
1859
  } else if (resolved_path) {
1410
1860
  free(resolved_path);
1861
+ if (section_name) free(section_name);
1411
1862
  }
1412
1863
  }
1413
1864
  }
@@ -1427,6 +1878,9 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1427
1878
  percent_decode_inplace(filepath);
1428
1879
  parse_embedded_delimiter_override(filepath, &mmd_delimiter_override);
1429
1880
 
1881
+ char *section_name = NULL;
1882
+ split_section_fragment(filepath, &section_name);
1883
+
1430
1884
  /* Check for address specification [address] */
1431
1885
  const char *address_start = filepath_end + 2;
1432
1886
  const char *address_end = NULL;
@@ -1457,8 +1911,11 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1457
1911
  apex_file_type_t file_type = apex_detect_file_type(resolved_path);
1458
1912
  char *content = read_file_contents(resolved_path);
1459
1913
  if (content) {
1914
+ char *section_content = section_name ? extract_markdown_section(content, section_name) : NULL;
1915
+ char *section_base = section_content ? section_content : strdup(content);
1916
+
1460
1917
  /* Extract metadata from original file content FIRST (before any processing) */
1461
- char *file_content_for_metadata = strdup(content);
1918
+ char *file_content_for_metadata = section_base ? strdup(section_base) : NULL;
1462
1919
  apex_metadata_item *file_metadata = NULL;
1463
1920
  char *file_text_after_metadata = file_content_for_metadata;
1464
1921
  if (file_content_for_metadata) {
@@ -1466,12 +1923,12 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1466
1923
  }
1467
1924
 
1468
1925
  /* Apply address specification if present */
1469
- char *extracted_content = content;
1926
+ char *extracted_content = section_base ? section_base : content;
1470
1927
  bool free_extracted = false;
1471
1928
 
1472
1929
  if (address_spec) {
1473
- extracted_content = extract_lines(content, address_spec);
1474
- if (extracted_content && extracted_content != content) {
1930
+ extracted_content = extract_lines(section_base ? section_base : content, address_spec);
1931
+ if (extracted_content && extracted_content != (section_base ? section_base : content)) {
1475
1932
  free_extracted = true;
1476
1933
  }
1477
1934
  }
@@ -1492,6 +1949,15 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1492
1949
  }
1493
1950
  }
1494
1951
 
1952
+ if (file_type == FILE_TYPE_MARKDOWN || file_type == FILE_TYPE_TEXT) {
1953
+ char *plugin_text = run_preparse_plugins_on_text(to_process, plugin_manager, options);
1954
+ if (plugin_text) {
1955
+ if (free_to_process) free(to_process);
1956
+ to_process = plugin_text;
1957
+ free_to_process = true;
1958
+ }
1959
+ }
1960
+
1495
1961
  /* Get transclude base from file's metadata, or use file's directory */
1496
1962
  char *transclude_base = NULL;
1497
1963
  if (file_metadata) {
@@ -1502,7 +1968,13 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1502
1968
  }
1503
1969
 
1504
1970
  /* Recursively process with file's metadata and transclude base */
1505
- char *processed = apex_process_includes(to_process, transclude_base, file_metadata, depth + 1);
1971
+ char *processed = apex_process_includes(to_process,
1972
+ transclude_base,
1973
+ file_metadata,
1974
+ depth + 1,
1975
+ default_extension,
1976
+ plugin_manager,
1977
+ options);
1506
1978
 
1507
1979
  /* Cleanup */
1508
1980
  if (transclude_base) free(transclude_base);
@@ -1522,6 +1994,7 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1522
1994
  if (free_to_process) free(to_process);
1523
1995
  if (free_extracted) free(extracted_content);
1524
1996
  free(content);
1997
+ if (section_base) free(section_base);
1525
1998
 
1526
1999
  if (address_end) {
1527
2000
  read_pos = address_end + 1;
@@ -1531,12 +2004,15 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1531
2004
  free(resolved_path);
1532
2005
  if (address_spec) free_address_spec(address_spec);
1533
2006
  processed_include = true;
2007
+ if (section_name) free(section_name);
1534
2008
  } else {
1535
2009
  free(resolved_path);
1536
2010
  if (address_spec) free_address_spec(address_spec);
2011
+ if (section_name) free(section_name);
1537
2012
  }
1538
2013
  } else {
1539
2014
  if (address_spec) free_address_spec(address_spec);
2015
+ if (section_name) free(section_name);
1540
2016
  }
1541
2017
  }
1542
2018
  }
@@ -1580,10 +2056,14 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1580
2056
  parse_embedded_delimiter_override(filepath, &marked_delimiter_override);
1581
2057
  }
1582
2058
 
1583
- /* Check for address specification [address] */
2059
+ char *section_name = NULL;
2060
+ split_section_fragment(filepath, &section_name);
2061
+
2062
+ /* Check for address specification [address] on the SAME line only.
2063
+ * Do not skip across newlines, otherwise following reference
2064
+ * definitions like "[^1]: ..." are misparsed as include addresses. */
1584
2065
  const char *address_start = filepath_end + 1;
1585
- /* Skip whitespace */
1586
- while (*address_start && isspace(*address_start)) address_start++;
2066
+ while (*address_start == ' ' || *address_start == '\t') address_start++;
1587
2067
  const char *address_end = NULL;
1588
2068
  address_spec_t *address_spec = NULL;
1589
2069
 
@@ -1607,8 +2087,11 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1607
2087
  apex_file_type_t file_type = apex_detect_file_type(resolved_path);
1608
2088
  char *content = read_file_contents(resolved_path);
1609
2089
  if (content) {
2090
+ char *section_content = section_name ? extract_markdown_section(content, section_name) : NULL;
2091
+ char *section_base = section_content ? section_content : strdup(content);
2092
+
1610
2093
  /* Extract metadata from original file content FIRST (before any processing) */
1611
- char *file_content_for_metadata = strdup(content);
2094
+ char *file_content_for_metadata = section_base ? strdup(section_base) : NULL;
1612
2095
  apex_metadata_item *file_metadata = NULL;
1613
2096
  char *file_text_after_metadata = file_content_for_metadata;
1614
2097
  if (file_content_for_metadata) {
@@ -1616,12 +2099,12 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1616
2099
  }
1617
2100
 
1618
2101
  /* Apply address specification if present */
1619
- char *extracted_content = content;
2102
+ char *extracted_content = section_base ? section_base : content;
1620
2103
  bool free_extracted = false;
1621
2104
 
1622
2105
  if (address_spec) {
1623
- extracted_content = extract_lines(content, address_spec);
1624
- if (extracted_content && extracted_content != content) {
2106
+ extracted_content = extract_lines(section_base ? section_base : content, address_spec);
2107
+ if (extracted_content && extracted_content != (section_base ? section_base : content)) {
1625
2108
  free_extracted = true;
1626
2109
  }
1627
2110
  }
@@ -1643,6 +2126,14 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1643
2126
  free_to_process = true;
1644
2127
  }
1645
2128
  }
2129
+ if (file_type == FILE_TYPE_MARKDOWN || file_type == FILE_TYPE_TEXT) {
2130
+ char *plugin_text = run_preparse_plugins_on_text(to_process, plugin_manager, options);
2131
+ if (plugin_text) {
2132
+ if (free_to_process) free(to_process);
2133
+ to_process = plugin_text;
2134
+ free_to_process = true;
2135
+ }
2136
+ }
1646
2137
 
1647
2138
  /* Markdown include - recursively process */
1648
2139
 
@@ -1655,7 +2146,13 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1655
2146
  transclude_base = get_directory(resolved_path);
1656
2147
  }
1657
2148
 
1658
- char *processed = apex_process_includes(to_process, transclude_base, file_metadata, depth + 1);
2149
+ char *processed = apex_process_includes(to_process,
2150
+ transclude_base,
2151
+ file_metadata,
2152
+ depth + 1,
2153
+ default_extension,
2154
+ plugin_manager,
2155
+ options);
1659
2156
 
1660
2157
  /* Cleanup */
1661
2158
  if (transclude_base) free(transclude_base);
@@ -1718,6 +2215,7 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1718
2215
 
1719
2216
  if (free_extracted) free(extracted_content);
1720
2217
  free(content);
2218
+ if (section_base) free(section_base);
1721
2219
  }
1722
2220
  free(resolved_path);
1723
2221
  }
@@ -1741,6 +2239,7 @@ char *apex_process_includes(const char *text, const char *base_dir, apex_metadat
1741
2239
  }
1742
2240
  }
1743
2241
  if (address_spec) free_address_spec(address_spec);
2242
+ if (section_name) free(section_name);
1744
2243
  processed_include = true;
1745
2244
  }
1746
2245
  }