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.
- checksums.yaml +4 -4
- data/README.md +17 -3
- data/ext/apex_ext/apex_ext.c +260 -91
- data/ext/apex_ext/apex_src/CHANGELOG.md +440 -0
- data/ext/apex_ext/apex_src/CMakeLists.txt +15 -1
- data/ext/apex_ext/apex_src/Formula/apex.rb +7 -2
- data/ext/apex_ext/apex_src/Package.swift +15 -4
- data/ext/apex_ext/apex_src/README.md +94 -35
- data/ext/apex_ext/apex_src/VERSION +1 -1
- data/ext/apex_ext/apex_src/apex-debug.log +4 -0
- data/ext/apex_ext/apex_src/apex.code-workspace +38 -0
- data/ext/apex_ext/apex_src/apex.taskpaper +12 -0
- data/ext/apex_ext/apex_src/apex_deflist_debug.log +25 -0
- data/ext/apex_ext/apex_src/buildnotes.md +1435 -0
- data/ext/apex_ext/apex_src/cli/main.c +262 -33
- data/ext/apex_ext/apex_src/debug.log +7 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +274 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +749 -0
- data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +744 -0
- data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +382 -0
- data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +351 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +745 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +829 -0
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +1148 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +161 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +184 -0
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +211 -0
- data/ext/apex_ext/apex_src/documentation/app-settings-table.md +67 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +340 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +11 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +234 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +516 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +461 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +193 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +201 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +678 -0
- data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +1367 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +6 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +774 -300
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +1194 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +416 -216
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3544 -1094
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +371 -97
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +153 -23
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +358 -124
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +1656 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +1226 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +201 -56
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +279 -109
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +200 -53
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +2327 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +690 -271
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +201 -55
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +238 -68
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +421 -221
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +340 -78
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +251 -86
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +1362 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +277 -90
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +673 -346
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +1405 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +1902 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +1939 -1698
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +1781 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +315 -140
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +307 -113
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +1132 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +1754 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +872 -376
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +8746 -0
- data/ext/apex_ext/apex_src/documentation/shared_styles.css +20 -15
- data/ext/apex_ext/apex_src/documentation/transform_for_app.md +55 -0
- data/ext/apex_ext/apex_src/escaping-repro.md +103 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +8 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/QuickLook/Preview.png +0 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/document.wflow +547 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown-to-RTF.workflow.zip +0 -0
- data/ext/apex_ext/apex_src/examples/quick-actions/README.md +33 -0
- data/ext/apex_ext/apex_src/include/apex/apex.h +92 -6
- data/ext/apex_ext/apex_src/include/apex/ast_rtf.h +23 -0
- data/ext/apex_ext/apex_src/include/apex/ast_terminal.h +6 -0
- data/ext/apex_ext/apex_src/include/apex/module.modulemap +5 -0
- data/ext/apex_ext/apex_src/include/apex/plugins.h +105 -0
- data/ext/apex_ext/apex_src/include/apexc.h +1 -0
- data/ext/apex_ext/apex_src/man/apex.1 +82 -78
- data/ext/apex_ext/apex_src/man/apex.1.md +81 -4
- data/ext/apex_ext/apex_src/objc/Apex.swift +73 -0
- data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +79 -0
- data/ext/apex_ext/apex_src/objc/NSString+Apex.h +83 -5
- data/ext/apex_ext/apex_src/objc/NSString+Apex.m +244 -1
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +76 -0
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +169 -0
- data/ext/apex_ext/apex_src/output.html +87 -0
- data/ext/apex_ext/apex_src/plans/quarto-mode.md +442 -0
- data/ext/apex_ext/apex_src/src/_README.md +85 -32
- data/ext/apex_ext/apex_src/src/apex.c +770 -200
- data/ext/apex_ext/apex_src/src/ast_rtf.c +848 -0
- data/ext/apex_ext/apex_src/src/ast_terminal.c +184 -7
- data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +10 -3
- data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +7 -11
- data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.c +467 -0
- data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.h +27 -0
- data/ext/apex_ext/apex_src/src/extensions/callouts.c +400 -4
- data/ext/apex_ext/apex_src/src/extensions/callouts.h +15 -5
- data/ext/apex_ext/apex_src/src/extensions/citations.c +26 -5
- data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.c +632 -0
- data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.h +22 -0
- data/ext/apex_ext/apex_src/src/extensions/concordance.c +632 -0
- data/ext/apex_ext/apex_src/src/extensions/definition_list.c +129 -15
- data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1050 -121
- data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +569 -16
- data/ext/apex_ext/apex_src/src/extensions/ial.c +502 -52
- data/ext/apex_ext/apex_src/src/extensions/ial.h +2 -1
- data/ext/apex_ext/apex_src/src/extensions/includes.c +532 -33
- data/ext/apex_ext/apex_src/src/extensions/includes.h +7 -1
- data/ext/apex_ext/apex_src/src/extensions/index.c +1427 -140
- data/ext/apex_ext/apex_src/src/extensions/index.h +28 -0
- data/ext/apex_ext/apex_src/src/extensions/metadata.c +200 -161
- data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.c +251 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.h +22 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_lists.c +624 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_lists.h +35 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_polish.c +264 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_polish.h +23 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.c +307 -0
- data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.h +21 -0
- data/ext/apex_ext/apex_src/src/extensions/raw_content.c +351 -0
- data/ext/apex_ext/apex_src/src/extensions/raw_content.h +20 -0
- data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +117 -3
- data/ext/apex_ext/apex_src/src/extensions/special_markers.c +53 -0
- data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +23 -1
- data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +83 -92
- data/ext/apex_ext/apex_src/src/extensions/toc.c +446 -25
- data/ext/apex_ext/apex_src/src/extensions/toc.h +26 -1
- data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +28 -0
- data/ext/apex_ext/apex_src/src/html_renderer.c +387 -17
- data/ext/apex_ext/apex_src/src/plugin_catalog.c +517 -0
- data/ext/apex_ext/apex_src/src/plugins_remote.c +88 -14
- data/ext/apex_ext/apex_src/src/plugins_remote.h +34 -0
- data/ext/apex_ext/apex_src/src/pretty_html.c +6 -1
- data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +21 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +10 -0
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +19 -0
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +11 -0
- data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +2 -0
- data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +46 -0
- data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +47 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +3 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +3 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +8 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +19 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +7 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +81 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +5 -0
- data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +47 -0
- data/ext/apex_ext/apex_src/tests/fixtures/speed.md +192 -0
- data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +37 -0
- data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +16 -0
- data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +301 -0
- data/ext/apex_ext/apex_src/tests/test_concordance.tsv +4 -0
- data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +82 -0
- data/ext/apex_ext/apex_src/tests/test_extensions.c +821 -1
- data/ext/apex_ext/apex_src/tests/test_ial.c +7 -0
- data/ext/apex_ext/apex_src/tests/test_marked_integration.c +25 -0
- data/ext/apex_ext/apex_src/tests/test_metadata.c +129 -0
- data/ext/apex_ext/apex_src/tests/test_output.c +568 -2
- data/ext/apex_ext/apex_src/tests/test_runner.c +11 -0
- data/ext/apex_ext/apex_src/tests/test_tables.c +420 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +1 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +77 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +135 -5
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +56 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +38 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +1 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/cmark.cpython-314.pyc +0 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/normalize.cpython-314.pyc +0 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +69 -0
- data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +74 -0
- data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +22 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Apex +0 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Resources/Info.plist +44 -0
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +128 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +5118 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +99 -0
- data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +452 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +52 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +55 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +2455 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +171 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +55 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +220 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +43 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +7 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +76 -0
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +10 -0
- data/ext/apex_ext/extconf.rb +35 -0
- data/lib/apex/version.rb +2 -2
- metadata +117 -2
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
#include "extensions/math.h"
|
|
20
20
|
#include "extensions/critic.h"
|
|
21
21
|
#include "extensions/callouts.h"
|
|
22
|
+
#include "extensions/raw_content.h"
|
|
23
|
+
#include "extensions/code_fence_attrs.h"
|
|
24
|
+
#include "extensions/quarto_diagrams.h"
|
|
25
|
+
#include "extensions/quarto_shortcodes.h"
|
|
26
|
+
#include "extensions/quarto_polish.h"
|
|
27
|
+
#include "extensions/quarto_lists.h"
|
|
22
28
|
#include "extensions/includes.h"
|
|
23
29
|
#include "extensions/toc.h"
|
|
24
30
|
#include "extensions/abbreviations.h"
|
|
@@ -36,6 +42,7 @@
|
|
|
36
42
|
#include "extensions/sup_sub.h"
|
|
37
43
|
#include "extensions/header_ids.h"
|
|
38
44
|
#include "extensions/relaxed_tables.h"
|
|
45
|
+
#include "extensions/grid_tables.h"
|
|
39
46
|
#include "extensions/citations.h"
|
|
40
47
|
#include "extensions/index.h"
|
|
41
48
|
#include "extensions/fenced_divs.h"
|
|
@@ -45,6 +52,7 @@
|
|
|
45
52
|
#include "apex/ast_markdown.h"
|
|
46
53
|
#include "apex/ast_terminal.h"
|
|
47
54
|
#include "apex/ast_man.h"
|
|
55
|
+
#include "apex/ast_rtf.h"
|
|
48
56
|
#include "filters_ast.h"
|
|
49
57
|
|
|
50
58
|
/* Custom renderer */
|
|
@@ -663,6 +671,49 @@ static char *apex_embed_images(const char *html, const apex_options *options, co
|
|
|
663
671
|
return output;
|
|
664
672
|
}
|
|
665
673
|
|
|
674
|
+
/**
|
|
675
|
+
* Track HTML tag / quoted-attribute state while scanning source text.
|
|
676
|
+
* State reflects characters already consumed, matching the previous
|
|
677
|
+
* "rescan from document start" semantics without the O(n^2) cost.
|
|
678
|
+
*/
|
|
679
|
+
static void apex_autolink_html_consume(char c, bool *in_html_tag,
|
|
680
|
+
bool *html_quote_active,
|
|
681
|
+
char *html_quote_char) {
|
|
682
|
+
if (!*in_html_tag) {
|
|
683
|
+
if (c == '<') {
|
|
684
|
+
*in_html_tag = true;
|
|
685
|
+
*html_quote_active = false;
|
|
686
|
+
*html_quote_char = '\0';
|
|
687
|
+
}
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (*html_quote_active) {
|
|
692
|
+
if (c == *html_quote_char) {
|
|
693
|
+
*html_quote_active = false;
|
|
694
|
+
*html_quote_char = '\0';
|
|
695
|
+
}
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (c == '"' || c == '\'') {
|
|
700
|
+
*html_quote_active = true;
|
|
701
|
+
*html_quote_char = c;
|
|
702
|
+
} else if (c == '>') {
|
|
703
|
+
*in_html_tag = false;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
static void apex_autolink_html_consume_range(const char *s, size_t n,
|
|
708
|
+
bool *in_html_tag,
|
|
709
|
+
bool *html_quote_active,
|
|
710
|
+
char *html_quote_char) {
|
|
711
|
+
for (size_t i = 0; i < n; i++) {
|
|
712
|
+
apex_autolink_html_consume(s[i], in_html_tag, html_quote_active,
|
|
713
|
+
html_quote_char);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
666
717
|
/**
|
|
667
718
|
* Preprocess angle-bracket autolinks (<http://...>) into explicit links
|
|
668
719
|
* and convert bare URLs/emails to explicit links so they survive
|
|
@@ -687,6 +738,9 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
687
738
|
bool in_markdown_link_url = false; /* Track if we're inside [text](url) URL part */
|
|
688
739
|
int bracket_count = 0; /* Count unmatched [ for markdown links */
|
|
689
740
|
int paren_count = 0; /* Count unmatched ( inside markdown link URLs */
|
|
741
|
+
bool in_html_tag = false;
|
|
742
|
+
bool html_quote_active = false;
|
|
743
|
+
char html_quote_char = '\0';
|
|
690
744
|
|
|
691
745
|
while (*r) {
|
|
692
746
|
const char *loop_start = r;
|
|
@@ -694,16 +748,26 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
694
748
|
/* Handle Liquid tags: copy {% ... %} without processing */
|
|
695
749
|
if (!in_liquid && *r == '{' && r[1] == '%') {
|
|
696
750
|
in_liquid = true;
|
|
751
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
752
|
+
&html_quote_char);
|
|
697
753
|
*w++ = *r++;
|
|
754
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
755
|
+
&html_quote_char);
|
|
698
756
|
*w++ = *r++;
|
|
699
757
|
continue;
|
|
700
758
|
}
|
|
701
759
|
if (in_liquid) {
|
|
702
760
|
if (*r == '%' && r[1] == '}') {
|
|
761
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
762
|
+
&html_quote_char);
|
|
703
763
|
*w++ = *r++;
|
|
764
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
765
|
+
&html_quote_char);
|
|
704
766
|
*w++ = *r++;
|
|
705
767
|
in_liquid = false;
|
|
706
768
|
} else {
|
|
769
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
770
|
+
&html_quote_char);
|
|
707
771
|
*w++ = *r++;
|
|
708
772
|
}
|
|
709
773
|
continue;
|
|
@@ -733,6 +797,9 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
733
797
|
}
|
|
734
798
|
memcpy(w, r, line_len);
|
|
735
799
|
w += line_len;
|
|
800
|
+
apex_autolink_html_consume_range(r, line_len, &in_html_tag,
|
|
801
|
+
&html_quote_active,
|
|
802
|
+
&html_quote_char);
|
|
736
803
|
r = line_end;
|
|
737
804
|
continue;
|
|
738
805
|
}
|
|
@@ -762,6 +829,9 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
762
829
|
}
|
|
763
830
|
memcpy(w, r, line_len);
|
|
764
831
|
w += line_len;
|
|
832
|
+
apex_autolink_html_consume_range(r, line_len, &in_html_tag,
|
|
833
|
+
&html_quote_active,
|
|
834
|
+
&html_quote_char);
|
|
765
835
|
r = line_end;
|
|
766
836
|
continue;
|
|
767
837
|
}
|
|
@@ -788,17 +858,23 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
788
858
|
}
|
|
789
859
|
/* Copy the backticks */
|
|
790
860
|
for (int i = 0; i < backtick_count; i++) {
|
|
861
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
862
|
+
&html_quote_char);
|
|
791
863
|
*w++ = *r++;
|
|
792
864
|
}
|
|
793
865
|
continue;
|
|
794
866
|
} else if (backtick_count == 1) {
|
|
795
867
|
/* Inline code span - toggle state and copy the backtick */
|
|
796
868
|
in_inline_code = !in_inline_code;
|
|
869
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
870
|
+
&html_quote_char);
|
|
797
871
|
*w++ = *r++;
|
|
798
872
|
continue;
|
|
799
873
|
} else {
|
|
800
874
|
/* Multiple backticks but less than 3 - copy them as-is (shouldn't happen in valid markdown) */
|
|
801
875
|
for (int i = 0; i < backtick_count; i++) {
|
|
876
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
877
|
+
&html_quote_char);
|
|
802
878
|
*w++ = *r++;
|
|
803
879
|
}
|
|
804
880
|
continue;
|
|
@@ -807,6 +883,17 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
807
883
|
|
|
808
884
|
/* Skip processing inside code blocks or inline code */
|
|
809
885
|
if (in_code_block || in_inline_code) {
|
|
886
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
887
|
+
&html_quote_char);
|
|
888
|
+
*w++ = *r++;
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/* Skip any autolink processing while inside an HTML tag (<...>),
|
|
893
|
+
* including quoted attributes. */
|
|
894
|
+
if (in_html_tag) {
|
|
895
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
896
|
+
&html_quote_char);
|
|
810
897
|
*w++ = *r++;
|
|
811
898
|
continue;
|
|
812
899
|
}
|
|
@@ -820,7 +907,11 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
820
907
|
in_markdown_link_url = true;
|
|
821
908
|
paren_count = 1; /* Count the opening '(' */
|
|
822
909
|
/* Copy ']' and '(' */
|
|
910
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
911
|
+
&html_quote_char);
|
|
823
912
|
*w++ = *r++; /* ']' */
|
|
913
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
914
|
+
&html_quote_char);
|
|
824
915
|
*w++ = *r++; /* '(' */
|
|
825
916
|
continue;
|
|
826
917
|
}
|
|
@@ -837,95 +928,18 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
837
928
|
}
|
|
838
929
|
}
|
|
839
930
|
|
|
840
|
-
/* Skip autolinking
|
|
841
|
-
if (in_markdown_link_url) {
|
|
931
|
+
/* Skip autolinking while inside markdown link text [ ... ] */
|
|
932
|
+
if (bracket_count > 0 && !in_markdown_link_url) {
|
|
933
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
934
|
+
&html_quote_char);
|
|
842
935
|
*w++ = *r++;
|
|
843
936
|
continue;
|
|
844
937
|
}
|
|
845
938
|
|
|
846
|
-
/*
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const char *p = r - 1;
|
|
851
|
-
const char *tag_start = NULL;
|
|
852
|
-
const char *tag_end = NULL;
|
|
853
|
-
|
|
854
|
-
/* Find nearest < or > before r */
|
|
855
|
-
while (p >= text) {
|
|
856
|
-
if (*p == '>') {
|
|
857
|
-
tag_end = p;
|
|
858
|
-
break;
|
|
859
|
-
} else if (*p == '<') {
|
|
860
|
-
tag_start = p;
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
p--;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
/* If we're inside a tag (between < and >), check if we're in an attribute */
|
|
867
|
-
if (tag_start && (!tag_end || tag_start > tag_end)) {
|
|
868
|
-
/* Look backwards from r to find the nearest = sign within this tag */
|
|
869
|
-
p = r - 1;
|
|
870
|
-
const char *equals_pos = NULL;
|
|
871
|
-
while (p > tag_start) {
|
|
872
|
-
if (*p == '=') {
|
|
873
|
-
equals_pos = p;
|
|
874
|
-
break;
|
|
875
|
-
} else if (*p == '>') {
|
|
876
|
-
break;
|
|
877
|
-
}
|
|
878
|
-
p--;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
if (equals_pos) {
|
|
882
|
-
/* Check what comes after the = */
|
|
883
|
-
const char *after_equals = equals_pos + 1;
|
|
884
|
-
/* Skip whitespace */
|
|
885
|
-
while (after_equals < r && isspace((unsigned char)*after_equals)) {
|
|
886
|
-
after_equals++;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
if (after_equals < r) {
|
|
890
|
-
/* Check if it's a quoted attribute */
|
|
891
|
-
if (*after_equals == '"' || *after_equals == '\'') {
|
|
892
|
-
char quote = *after_equals;
|
|
893
|
-
const char *value_start = after_equals + 1;
|
|
894
|
-
|
|
895
|
-
/* If r is after the opening quote, check if we're inside */
|
|
896
|
-
if (r >= value_start) {
|
|
897
|
-
/* Look for the closing quote - scan forward from value_start */
|
|
898
|
-
const char *quote_end = value_start;
|
|
899
|
-
while (quote_end < r && *quote_end != quote && *quote_end != '\0') {
|
|
900
|
-
quote_end++;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/* If we haven't found the closing quote by the time we reach r, we're inside */
|
|
904
|
-
if (quote_end >= r || *quote_end != quote) {
|
|
905
|
-
in_html_attribute = true;
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
} else {
|
|
909
|
-
/* Unquoted attribute - value is between = and next space or > */
|
|
910
|
-
const char *value_start = after_equals;
|
|
911
|
-
if (r > value_start) {
|
|
912
|
-
const char *value_end = value_start;
|
|
913
|
-
while (value_end < r && !isspace((unsigned char)*value_end) && *value_end != '>') {
|
|
914
|
-
value_end++;
|
|
915
|
-
}
|
|
916
|
-
/* If r is in this unquoted value, we're inside an attribute */
|
|
917
|
-
if (r <= value_end) {
|
|
918
|
-
in_html_attribute = true;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
if (in_html_attribute) {
|
|
928
|
-
/* Inside HTML attribute, copy as-is */
|
|
939
|
+
/* Skip autolinking if we're inside a markdown link URL */
|
|
940
|
+
if (in_markdown_link_url) {
|
|
941
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
942
|
+
&html_quote_char);
|
|
929
943
|
*w++ = *r++;
|
|
930
944
|
continue;
|
|
931
945
|
}
|
|
@@ -953,6 +967,7 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
953
967
|
*w++ = ']'; *w++ = '(';
|
|
954
968
|
memcpy(w, start, url_len); w += url_len;
|
|
955
969
|
*w++ = ')';
|
|
970
|
+
/* Rewritten away from HTML; net tag state is unchanged. */
|
|
956
971
|
r = end + 1;
|
|
957
972
|
continue;
|
|
958
973
|
}
|
|
@@ -975,6 +990,9 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
975
990
|
}
|
|
976
991
|
memcpy(w, r, comment_len);
|
|
977
992
|
w += comment_len;
|
|
993
|
+
apex_autolink_html_consume_range(r, comment_len, &in_html_tag,
|
|
994
|
+
&html_quote_active,
|
|
995
|
+
&html_quote_char);
|
|
978
996
|
r = comment_end + 3;
|
|
979
997
|
continue;
|
|
980
998
|
}
|
|
@@ -984,87 +1002,7 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
984
1002
|
bool is_url_start = false;
|
|
985
1003
|
bool is_email_start = false;
|
|
986
1004
|
|
|
987
|
-
|
|
988
|
-
bool in_html_attribute_url = false;
|
|
989
|
-
if (!isspace((unsigned char)*r) && (*r == 'h' || *r == 'm' || *r == '@')) {
|
|
990
|
-
const char *p = r - 1;
|
|
991
|
-
const char *tag_start = NULL;
|
|
992
|
-
const char *tag_end = NULL;
|
|
993
|
-
|
|
994
|
-
/* Find nearest < or > before r */
|
|
995
|
-
while (p >= text) {
|
|
996
|
-
if (*p == '>') {
|
|
997
|
-
tag_end = p;
|
|
998
|
-
break;
|
|
999
|
-
} else if (*p == '<') {
|
|
1000
|
-
tag_start = p;
|
|
1001
|
-
break;
|
|
1002
|
-
}
|
|
1003
|
-
p--;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
/* If we're inside a tag (between < and >), check if we're in an attribute */
|
|
1007
|
-
if (tag_start && (!tag_end || tag_start > tag_end)) {
|
|
1008
|
-
/* Look backwards from r to find the nearest = sign within this tag */
|
|
1009
|
-
p = r - 1;
|
|
1010
|
-
const char *equals_pos = NULL;
|
|
1011
|
-
while (p > tag_start) {
|
|
1012
|
-
if (*p == '=') {
|
|
1013
|
-
equals_pos = p;
|
|
1014
|
-
break;
|
|
1015
|
-
} else if (*p == '>') {
|
|
1016
|
-
break;
|
|
1017
|
-
}
|
|
1018
|
-
p--;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
if (equals_pos) {
|
|
1022
|
-
/* Check what comes after the = */
|
|
1023
|
-
const char *after_equals = equals_pos + 1;
|
|
1024
|
-
/* Skip whitespace */
|
|
1025
|
-
while (after_equals < r && isspace((unsigned char)*after_equals)) {
|
|
1026
|
-
after_equals++;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
if (after_equals < r) {
|
|
1030
|
-
/* Check if it's a quoted attribute */
|
|
1031
|
-
if (*after_equals == '"' || *after_equals == '\'') {
|
|
1032
|
-
char quote = *after_equals;
|
|
1033
|
-
const char *value_start = after_equals + 1;
|
|
1034
|
-
|
|
1035
|
-
/* If r is after the opening quote, check if we're inside */
|
|
1036
|
-
if (r >= value_start) {
|
|
1037
|
-
/* Look for the closing quote - scan forward from value_start */
|
|
1038
|
-
const char *quote_end = value_start;
|
|
1039
|
-
while (quote_end < r && *quote_end != quote && *quote_end != '\0') {
|
|
1040
|
-
quote_end++;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
/* If we haven't found the closing quote by the time we reach r, we're inside */
|
|
1044
|
-
if (quote_end >= r || *quote_end != quote) {
|
|
1045
|
-
in_html_attribute_url = true;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
} else {
|
|
1049
|
-
/* Unquoted attribute - value is between = and next space or > */
|
|
1050
|
-
const char *value_start = after_equals;
|
|
1051
|
-
if (r > value_start) {
|
|
1052
|
-
const char *value_end = value_start;
|
|
1053
|
-
while (value_end < r && !isspace((unsigned char)*value_end) && *value_end != '>') {
|
|
1054
|
-
value_end++;
|
|
1055
|
-
}
|
|
1056
|
-
/* If r is in this unquoted value, we're inside an attribute */
|
|
1057
|
-
if (r <= value_end) {
|
|
1058
|
-
in_html_attribute_url = true;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
if (!isspace((unsigned char)*r) && !in_html_attribute_url) {
|
|
1005
|
+
if (!isspace((unsigned char)*r)) {
|
|
1068
1006
|
/* Check for URL protocols */
|
|
1069
1007
|
if (strncmp(r, "http://", 7) == 0 || strncmp(r, "https://", 8) == 0 || strncmp(r, "mailto:", 7) == 0) {
|
|
1070
1008
|
is_url_start = true;
|
|
@@ -1090,6 +1028,30 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
1090
1028
|
/* Require that the character immediately before @ is alphanumeric.
|
|
1091
1029
|
* This prevents matching @ in URLs like https://example.com/@user */
|
|
1092
1030
|
if (at_pos > r && isalnum((unsigned char)at_pos[-1])) {
|
|
1031
|
+
/* Ignore image-density suffixes like @2x.jpg, @3x.avif, etc. */
|
|
1032
|
+
const char *suffix = at_pos + 1;
|
|
1033
|
+
if (suffix < token_end && isdigit((unsigned char)*suffix)) {
|
|
1034
|
+
while (suffix < token_end && isdigit((unsigned char)*suffix)) {
|
|
1035
|
+
suffix++;
|
|
1036
|
+
}
|
|
1037
|
+
if (suffix < token_end && *suffix == 'x') {
|
|
1038
|
+
suffix++;
|
|
1039
|
+
if (suffix < token_end && *suffix == '.') {
|
|
1040
|
+
suffix++;
|
|
1041
|
+
if (suffix < token_end && isalnum((unsigned char)*suffix)) {
|
|
1042
|
+
while (suffix < token_end && isalnum((unsigned char)*suffix)) {
|
|
1043
|
+
suffix++;
|
|
1044
|
+
}
|
|
1045
|
+
if (suffix == token_end) {
|
|
1046
|
+
/* This token is an image filename density suffix,
|
|
1047
|
+
* not an email address. */
|
|
1048
|
+
goto skip_email_candidate;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1093
1055
|
/* Validate that email has a TLD (at least one dot followed by alphanumeric) */
|
|
1094
1056
|
const char *after_at = at_pos + 1;
|
|
1095
1057
|
bool has_tld = false;
|
|
@@ -1113,6 +1075,7 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
1113
1075
|
}
|
|
1114
1076
|
}
|
|
1115
1077
|
}
|
|
1078
|
+
skip_email_candidate:
|
|
1116
1079
|
|
|
1117
1080
|
if (is_url_start || is_email_start) {
|
|
1118
1081
|
const char *start = r;
|
|
@@ -1170,6 +1133,9 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
1170
1133
|
*w++ = ']'; *w++ = '(';
|
|
1171
1134
|
memcpy(w, href_text, href_len); w += href_len;
|
|
1172
1135
|
*w++ = ')';
|
|
1136
|
+
apex_autolink_html_consume_range(start, (size_t)(end - start),
|
|
1137
|
+
&in_html_tag, &html_quote_active,
|
|
1138
|
+
&html_quote_char);
|
|
1173
1139
|
r = end;
|
|
1174
1140
|
free(mailto_buf);
|
|
1175
1141
|
continue;
|
|
@@ -1177,6 +1143,8 @@ static char *apex_preprocess_autolinks(const char *text, const apex_options *opt
|
|
|
1177
1143
|
free(mailto_buf);
|
|
1178
1144
|
}
|
|
1179
1145
|
|
|
1146
|
+
apex_autolink_html_consume(*r, &in_html_tag, &html_quote_active,
|
|
1147
|
+
&html_quote_char);
|
|
1180
1148
|
*w++ = *r++;
|
|
1181
1149
|
|
|
1182
1150
|
/* Safety: ensure we always advance */
|
|
@@ -2207,9 +2175,13 @@ static char *apex_preprocess_table_colspans(const char *text) {
|
|
|
2207
2175
|
|
|
2208
2176
|
/**
|
|
2209
2177
|
* Preprocess alpha list markers (a., b., c. and A., B., C.)
|
|
2210
|
-
* Converts them to numbered markers (1., 2., 3.) and
|
|
2178
|
+
* Converts them to numbered markers (1., 2., 3.) and inserts an HTML comment (indented like
|
|
2179
|
+
* the list items) so the HTML pass can apply list-style-type; bracket tokens are not used
|
|
2180
|
+
* because the Markdown parser strips them before HTML is produced.
|
|
2211
2181
|
*/
|
|
2212
|
-
static char *apex_preprocess_alpha_lists(const char *text
|
|
2182
|
+
static char *apex_preprocess_alpha_lists(const char *text,
|
|
2183
|
+
bool *inserted_synthetic_nested_alpha_break,
|
|
2184
|
+
bool *saw_explicit_nested_alpha_break) {
|
|
2213
2185
|
if (!text) return NULL;
|
|
2214
2186
|
|
|
2215
2187
|
size_t text_len = strlen(text);
|
|
@@ -2227,6 +2199,8 @@ static char *apex_preprocess_alpha_lists(const char *text) {
|
|
|
2227
2199
|
bool is_upper = false;
|
|
2228
2200
|
int item_number = 1;
|
|
2229
2201
|
int blank_lines_since_alpha = 0; /* Track blank lines to detect list breaks */
|
|
2202
|
+
if (inserted_synthetic_nested_alpha_break) *inserted_synthetic_nested_alpha_break = false;
|
|
2203
|
+
if (saw_explicit_nested_alpha_break) *saw_explicit_nested_alpha_break = false;
|
|
2230
2204
|
|
|
2231
2205
|
while (*read) {
|
|
2232
2206
|
const char *line_start = read;
|
|
@@ -2261,6 +2235,14 @@ static char *apex_preprocess_alpha_lists(const char *text) {
|
|
|
2261
2235
|
}
|
|
2262
2236
|
|
|
2263
2237
|
if (is_alpha_marker) {
|
|
2238
|
+
bool nested_alpha_after_item = false;
|
|
2239
|
+
if (in_alpha_list && alpha_is_upper == is_upper && current_indent > alpha_list_indent) {
|
|
2240
|
+
if (alpha_is_upper) {
|
|
2241
|
+
nested_alpha_after_item = (alpha_char == expected_upper);
|
|
2242
|
+
} else {
|
|
2243
|
+
nested_alpha_after_item = (alpha_char == expected_lower);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2264
2246
|
/* Check if this continues an existing alpha list */
|
|
2265
2247
|
bool continues_list = false;
|
|
2266
2248
|
if (in_alpha_list) {
|
|
@@ -2278,6 +2260,13 @@ static char *apex_preprocess_alpha_lists(const char *text) {
|
|
|
2278
2260
|
}
|
|
2279
2261
|
|
|
2280
2262
|
if (!continues_list) {
|
|
2263
|
+
if (nested_alpha_after_item) {
|
|
2264
|
+
if (blank_lines_since_alpha > 0) {
|
|
2265
|
+
if (saw_explicit_nested_alpha_break) *saw_explicit_nested_alpha_break = true;
|
|
2266
|
+
} else {
|
|
2267
|
+
if (inserted_synthetic_nested_alpha_break) *inserted_synthetic_nested_alpha_break = true;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2281
2270
|
/* Start new alpha list */
|
|
2282
2271
|
in_alpha_list = true;
|
|
2283
2272
|
is_upper = alpha_is_upper;
|
|
@@ -2289,8 +2278,10 @@ static char *apex_preprocess_alpha_lists(const char *text) {
|
|
|
2289
2278
|
} else {
|
|
2290
2279
|
expected_lower = alpha_char;
|
|
2291
2280
|
}
|
|
2292
|
-
/*
|
|
2293
|
-
|
|
2281
|
+
/* HTML comment + indent: bracket markers are stripped by the parser; comments survive in unsafe HTML
|
|
2282
|
+
* and must be indented like the following list items to stay inside a parent <li>. */
|
|
2283
|
+
int needed = snprintf(write, remaining, "%.*s<!-- apex-alpha-list-%s -->\n\n",
|
|
2284
|
+
(int)(p - line_start), line_start, alpha_is_upper ? "upper" : "lower");
|
|
2294
2285
|
if (needed > 0 && needed < (int)remaining) {
|
|
2295
2286
|
write += needed;
|
|
2296
2287
|
remaining -= needed;
|
|
@@ -2407,7 +2398,9 @@ static char *apex_preprocess_alpha_lists(const char *text) {
|
|
|
2407
2398
|
* Insert a blank line before indented ordered sublists that directly follow
|
|
2408
2399
|
* a parent list item line, so they parse as nested <ol> blocks.
|
|
2409
2400
|
*/
|
|
2410
|
-
static char *apex_preprocess_nested_ordered_sublists(const char *text
|
|
2401
|
+
static char *apex_preprocess_nested_ordered_sublists(const char *text,
|
|
2402
|
+
bool *inserted_synthetic_break,
|
|
2403
|
+
bool *saw_explicit_break) {
|
|
2411
2404
|
if (!text) return NULL;
|
|
2412
2405
|
|
|
2413
2406
|
size_t text_len = strlen(text);
|
|
@@ -2423,6 +2416,9 @@ static char *apex_preprocess_nested_ordered_sublists(const char *text) {
|
|
|
2423
2416
|
bool prev_line_was_list_item = false;
|
|
2424
2417
|
size_t prev_line_indent = 0;
|
|
2425
2418
|
|
|
2419
|
+
if (inserted_synthetic_break) *inserted_synthetic_break = false;
|
|
2420
|
+
if (saw_explicit_break) *saw_explicit_break = false;
|
|
2421
|
+
|
|
2426
2422
|
while (*read) {
|
|
2427
2423
|
const char *line_start = read;
|
|
2428
2424
|
const char *line_end = strchr(read, '\n');
|
|
@@ -2454,8 +2450,14 @@ static char *apex_preprocess_nested_ordered_sublists(const char *text) {
|
|
|
2454
2450
|
}
|
|
2455
2451
|
}
|
|
2456
2452
|
|
|
2457
|
-
|
|
2458
|
-
|
|
2453
|
+
bool nested_ordered_after_item = (prev_line_was_list_item &&
|
|
2454
|
+
current_line_ordered_marker &&
|
|
2455
|
+
current_indent > prev_line_indent);
|
|
2456
|
+
if (nested_ordered_after_item && prev_line_was_blank) {
|
|
2457
|
+
if (saw_explicit_break) *saw_explicit_break = true;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
if (!prev_line_was_blank && nested_ordered_after_item) {
|
|
2459
2461
|
if (remaining < 1) {
|
|
2460
2462
|
size_t used = (size_t)(write - output);
|
|
2461
2463
|
size_t new_capacity = output_capacity * 2 + 64;
|
|
@@ -2471,6 +2473,7 @@ static char *apex_preprocess_nested_ordered_sublists(const char *text) {
|
|
|
2471
2473
|
}
|
|
2472
2474
|
*write++ = '\n';
|
|
2473
2475
|
remaining--;
|
|
2476
|
+
if (inserted_synthetic_break) *inserted_synthetic_break = true;
|
|
2474
2477
|
prev_line_was_blank = true;
|
|
2475
2478
|
}
|
|
2476
2479
|
|
|
@@ -2507,6 +2510,35 @@ static char *apex_preprocess_nested_ordered_sublists(const char *text) {
|
|
|
2507
2510
|
return output;
|
|
2508
2511
|
}
|
|
2509
2512
|
|
|
2513
|
+
/* Force tight rendering for lists synthesized from nested ordered sublist fixes. */
|
|
2514
|
+
static void apex_tighten_nested_ordered_list_items(cmark_node *node) {
|
|
2515
|
+
if (!node) return;
|
|
2516
|
+
|
|
2517
|
+
for (cmark_node *child = cmark_node_first_child(node); child; ) {
|
|
2518
|
+
cmark_node *next = cmark_node_next(child);
|
|
2519
|
+
apex_tighten_nested_ordered_list_items(child);
|
|
2520
|
+
child = next;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
if (cmark_node_get_type(node) != CMARK_NODE_LIST) return;
|
|
2524
|
+
|
|
2525
|
+
bool has_para_then_sublist_item = false;
|
|
2526
|
+
for (cmark_node *item = cmark_node_first_child(node); item; item = cmark_node_next(item)) {
|
|
2527
|
+
if (cmark_node_get_type(item) != CMARK_NODE_ITEM) continue;
|
|
2528
|
+
cmark_node *first = cmark_node_first_child(item);
|
|
2529
|
+
cmark_node *second = first ? cmark_node_next(first) : NULL;
|
|
2530
|
+
if (first && second &&
|
|
2531
|
+
cmark_node_get_type(first) == CMARK_NODE_PARAGRAPH &&
|
|
2532
|
+
cmark_node_get_type(second) == CMARK_NODE_LIST) {
|
|
2533
|
+
has_para_then_sublist_item = true;
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
if (has_para_then_sublist_item) {
|
|
2538
|
+
cmark_node_set_list_tight(node, 1);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2510
2542
|
/**
|
|
2511
2543
|
* Post-process HTML to add style attributes to alpha lists
|
|
2512
2544
|
* Finds HTML comments like <!-- apex-alpha-list-lower --> or <!-- apex-alpha-list-upper -->
|
|
@@ -2516,9 +2548,10 @@ static char *apex_preprocess_nested_ordered_sublists(const char *text) {
|
|
|
2516
2548
|
static char *apex_postprocess_alpha_lists_html(const char *html) {
|
|
2517
2549
|
if (!html) return NULL;
|
|
2518
2550
|
|
|
2519
|
-
/* Early exit:
|
|
2520
|
-
if (strstr(html, "[apex-alpha-list:") == NULL
|
|
2521
|
-
|
|
2551
|
+
/* Early exit: bracket markers (legacy) or HTML comments from preprocessor */
|
|
2552
|
+
if (strstr(html, "[apex-alpha-list:") == NULL &&
|
|
2553
|
+
strstr(html, "<!-- apex-alpha-list-") == NULL) {
|
|
2554
|
+
return NULL;
|
|
2522
2555
|
}
|
|
2523
2556
|
|
|
2524
2557
|
size_t html_len = strlen(html);
|
|
@@ -2546,9 +2579,78 @@ static char *apex_postprocess_alpha_lists_html(const char *html) {
|
|
|
2546
2579
|
} \
|
|
2547
2580
|
} while(0)
|
|
2548
2581
|
|
|
2549
|
-
/* Single-pass
|
|
2550
|
-
const char *read_start = read;
|
|
2582
|
+
/* Single-pass: HTML comments (current), <p>[apex-alpha-list:…]</p> (legacy), or stray brackets */
|
|
2583
|
+
const char *read_start = read;
|
|
2584
|
+
static const char alpha_comment_lower[] = "<!-- apex-alpha-list-lower -->";
|
|
2585
|
+
static const char alpha_comment_upper[] = "<!-- apex-alpha-list-upper -->";
|
|
2586
|
+
const size_t alpha_comment_lower_len = sizeof(alpha_comment_lower) - 1;
|
|
2587
|
+
const size_t alpha_comment_upper_len = sizeof(alpha_comment_upper) - 1;
|
|
2588
|
+
|
|
2551
2589
|
while (*read) {
|
|
2590
|
+
/* Strip HTML comment markers and style the following <ol> (not copied to output). */
|
|
2591
|
+
bool comment_upper = false;
|
|
2592
|
+
size_t comment_len = 0;
|
|
2593
|
+
if ((size_t)(html + html_len - read) >= alpha_comment_lower_len &&
|
|
2594
|
+
strncmp(read, alpha_comment_lower, alpha_comment_lower_len) == 0) {
|
|
2595
|
+
comment_len = alpha_comment_lower_len;
|
|
2596
|
+
} else if ((size_t)(html + html_len - read) >= alpha_comment_upper_len &&
|
|
2597
|
+
strncmp(read, alpha_comment_upper, alpha_comment_upper_len) == 0) {
|
|
2598
|
+
comment_upper = true;
|
|
2599
|
+
comment_len = alpha_comment_upper_len;
|
|
2600
|
+
}
|
|
2601
|
+
if (comment_len > 0) {
|
|
2602
|
+
size_t copy_len = (size_t)(read - read_start);
|
|
2603
|
+
ENSURE_SPACE(copy_len);
|
|
2604
|
+
if (copy_len > 0) {
|
|
2605
|
+
memcpy(write, read_start, copy_len);
|
|
2606
|
+
write += copy_len;
|
|
2607
|
+
remaining -= copy_len;
|
|
2608
|
+
}
|
|
2609
|
+
read += comment_len;
|
|
2610
|
+
read_start = read;
|
|
2611
|
+
while (*read == ' ' || *read == '\t' || *read == '\n' || *read == '\r') {
|
|
2612
|
+
read++;
|
|
2613
|
+
}
|
|
2614
|
+
read_start = read;
|
|
2615
|
+
if (read[0] == '<' && read[1] == 'o' && read[2] == 'l' &&
|
|
2616
|
+
(read[3] == '>' || read[3] == ' ' || read[3] == '\t' || read[3] == '\n')) {
|
|
2617
|
+
const char *ol_start = read;
|
|
2618
|
+
const char *tag_end = strchr(ol_start, '>');
|
|
2619
|
+
|
|
2620
|
+
if (tag_end) {
|
|
2621
|
+
bool has_style = false;
|
|
2622
|
+
for (const char *p = ol_start; p < tag_end; p++) {
|
|
2623
|
+
if (strncmp(p, "style=", 6) == 0) {
|
|
2624
|
+
has_style = true;
|
|
2625
|
+
break;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
size_t tag_len = (size_t)(tag_end - ol_start); /* before '>' */
|
|
2629
|
+
ENSURE_SPACE(tag_len + 50);
|
|
2630
|
+
memcpy(write, ol_start, tag_len);
|
|
2631
|
+
write += tag_len;
|
|
2632
|
+
remaining -= tag_len;
|
|
2633
|
+
if (!has_style) {
|
|
2634
|
+
const char *style = comment_upper
|
|
2635
|
+
? " style=\"list-style-type: upper-alpha\">"
|
|
2636
|
+
: " style=\"list-style-type: lower-alpha\">";
|
|
2637
|
+
size_t style_len = strlen(style);
|
|
2638
|
+
memcpy(write, style, style_len);
|
|
2639
|
+
write += style_len;
|
|
2640
|
+
remaining -= style_len;
|
|
2641
|
+
} else {
|
|
2642
|
+
ENSURE_SPACE(1);
|
|
2643
|
+
*write++ = '>';
|
|
2644
|
+
remaining--;
|
|
2645
|
+
}
|
|
2646
|
+
read = tag_end + 1;
|
|
2647
|
+
read_start = read;
|
|
2648
|
+
continue;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
continue;
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2552
2654
|
/* Drop any stray raw markers that did not form a standalone paragraph. */
|
|
2553
2655
|
if (strncmp(read, "[apex-alpha-list:lower]", 23) == 0 ||
|
|
2554
2656
|
strncmp(read, "[apex-alpha-list:upper]", 23) == 0) {
|
|
@@ -2823,9 +2925,22 @@ apex_options apex_options_default(void) {
|
|
|
2823
2925
|
opts.enable_task_lists = true;
|
|
2824
2926
|
opts.enable_attributes = true;
|
|
2825
2927
|
opts.enable_callouts = true;
|
|
2928
|
+
opts.enable_py_callouts = false;
|
|
2929
|
+
opts.enable_quarto_callouts = false;
|
|
2930
|
+
opts.enable_quarto_extensions = false;
|
|
2931
|
+
opts.enable_quarto_raw = false;
|
|
2932
|
+
opts.enable_quarto_example_lists = false;
|
|
2933
|
+
opts.enable_quarto_line_blocks = false;
|
|
2934
|
+
opts.enable_quarto_roman_lists = false;
|
|
2935
|
+
opts.enable_quarto_code_attrs = false;
|
|
2936
|
+
opts.enable_quarto_diagrams = false;
|
|
2937
|
+
opts.enable_quarto_shortcodes = false;
|
|
2938
|
+
opts.enable_quarto_strict_lists = false;
|
|
2939
|
+
opts.enable_quarto_xrefs = false;
|
|
2826
2940
|
opts.enable_marked_extensions = true;
|
|
2827
2941
|
opts.enable_divs = true; /* Enabled by default in unified mode */
|
|
2828
2942
|
opts.enable_spans = true; /* Enabled by default in unified mode */
|
|
2943
|
+
opts.enable_grid_tables = false; /* Disabled by default - enable with --grid-tables */
|
|
2829
2944
|
|
|
2830
2945
|
/* Critic markup mode (0=accept, 1=reject, 2=markup) */
|
|
2831
2946
|
opts.critic_mode = 2; /* Default: show markup */
|
|
@@ -2861,6 +2976,10 @@ apex_options apex_options_default(void) {
|
|
|
2861
2976
|
opts.generate_header_ids = true;
|
|
2862
2977
|
opts.header_anchors = false; /* Use header IDs by default, not anchor tags */
|
|
2863
2978
|
opts.id_format = 0; /* GFM format (with dashes) */
|
|
2979
|
+
opts.toc_min = 1;
|
|
2980
|
+
opts.toc_max = 3;
|
|
2981
|
+
opts.toc_entries_out = NULL;
|
|
2982
|
+
opts.toc_entries_count_out = NULL;
|
|
2864
2983
|
|
|
2865
2984
|
/* Table options */
|
|
2866
2985
|
opts.relaxed_tables = true; /* Default: enabled in unified mode (can be disabled with --no-relaxed-tables) */
|
|
@@ -2903,6 +3022,7 @@ apex_options apex_options_default(void) {
|
|
|
2903
3022
|
opts.enable_leanpub_index_syntax = false;
|
|
2904
3023
|
opts.suppress_index = false;
|
|
2905
3024
|
opts.group_index_by_letter = true;
|
|
3025
|
+
opts.concordance_files = NULL;
|
|
2906
3026
|
|
|
2907
3027
|
/* Wiki link options */
|
|
2908
3028
|
opts.wikilink_space = 0; /* Default: dash (0=dash, 1=none, 2=underscore, 3=space) */
|
|
@@ -2960,6 +3080,7 @@ apex_options apex_options_default(void) {
|
|
|
2960
3080
|
opts.theme_name = NULL;
|
|
2961
3081
|
opts.terminal_width = 0;
|
|
2962
3082
|
opts.paginate = false;
|
|
3083
|
+
opts.paginate_symbols = false;
|
|
2963
3084
|
opts.terminal_inline_images = true;
|
|
2964
3085
|
opts.terminal_image_width = 50;
|
|
2965
3086
|
|
|
@@ -2986,6 +3107,8 @@ apex_options apex_options_for_mode(apex_mode_t mode) {
|
|
|
2986
3107
|
opts.enable_task_lists = false;
|
|
2987
3108
|
opts.enable_attributes = false;
|
|
2988
3109
|
opts.enable_callouts = false;
|
|
3110
|
+
opts.enable_py_callouts = false;
|
|
3111
|
+
opts.enable_quarto_callouts = false;
|
|
2989
3112
|
opts.enable_marked_extensions = false;
|
|
2990
3113
|
opts.enable_divs = false;
|
|
2991
3114
|
opts.enable_spans = false;
|
|
@@ -3021,6 +3144,8 @@ apex_options apex_options_for_mode(apex_mode_t mode) {
|
|
|
3021
3144
|
opts.enable_wiki_links = false;
|
|
3022
3145
|
opts.enable_attributes = false;
|
|
3023
3146
|
opts.enable_callouts = false;
|
|
3147
|
+
opts.enable_py_callouts = false;
|
|
3148
|
+
opts.enable_quarto_callouts = false;
|
|
3024
3149
|
opts.enable_marked_extensions = false;
|
|
3025
3150
|
opts.enable_divs = false;
|
|
3026
3151
|
opts.enable_spans = false;
|
|
@@ -3058,6 +3183,8 @@ apex_options apex_options_for_mode(apex_mode_t mode) {
|
|
|
3058
3183
|
opts.enable_task_lists = false;
|
|
3059
3184
|
opts.enable_attributes = false;
|
|
3060
3185
|
opts.enable_callouts = false;
|
|
3186
|
+
opts.enable_py_callouts = false;
|
|
3187
|
+
opts.enable_quarto_callouts = false;
|
|
3061
3188
|
opts.enable_marked_extensions = false;
|
|
3062
3189
|
opts.enable_divs = false;
|
|
3063
3190
|
opts.enable_spans = false;
|
|
@@ -3092,6 +3219,8 @@ apex_options apex_options_for_mode(apex_mode_t mode) {
|
|
|
3092
3219
|
opts.enable_task_lists = false;
|
|
3093
3220
|
opts.enable_attributes = true;
|
|
3094
3221
|
opts.enable_callouts = false;
|
|
3222
|
+
opts.enable_py_callouts = false;
|
|
3223
|
+
opts.enable_quarto_callouts = false;
|
|
3095
3224
|
/* Enable Marked-style extensions (including TOC) so that
|
|
3096
3225
|
* Kramdown documents can use <!--TOC--> and {:toc} syntax
|
|
3097
3226
|
* for table-of-contents generation. */
|
|
@@ -3133,16 +3262,142 @@ apex_options apex_options_for_mode(apex_mode_t mode) {
|
|
|
3133
3262
|
opts.enable_mmark_index_syntax = true; /* Unified: mmark index syntax */
|
|
3134
3263
|
opts.enable_textindex_syntax = true; /* Unified: TextIndex syntax enabled */
|
|
3135
3264
|
opts.enable_leanpub_index_syntax = true; /* Unified: Leanpub index syntax enabled */
|
|
3265
|
+
opts.enable_py_callouts = false; /* Unified default: off unless explicitly enabled */
|
|
3266
|
+
opts.enable_quarto_callouts = false; /* Unified default: off unless explicitly enabled */
|
|
3136
3267
|
opts.enable_divs = true; /* Unified: Pandoc fenced divs enabled */
|
|
3137
3268
|
opts.enable_spans = true; /* Unified: bracketed spans enabled */
|
|
3138
3269
|
opts.enable_emoji_autocorrect = true; /* Unified: emoji autocorrect enabled */
|
|
3139
3270
|
opts.enable_image_captions = true; /* Unified: image captions enabled by default */
|
|
3140
3271
|
break;
|
|
3272
|
+
|
|
3273
|
+
case APEX_MODE_QUARTO:
|
|
3274
|
+
/* Pandoc/Quarto markdown: unified-family defaults tuned for Quarto docs */
|
|
3275
|
+
opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3276
|
+
opts.mode = APEX_MODE_QUARTO;
|
|
3277
|
+
opts.enable_quarto_extensions = true;
|
|
3278
|
+
opts.enable_quarto_raw = true;
|
|
3279
|
+
opts.enable_quarto_example_lists = true;
|
|
3280
|
+
opts.enable_quarto_line_blocks = true;
|
|
3281
|
+
opts.enable_quarto_roman_lists = true;
|
|
3282
|
+
opts.enable_quarto_code_attrs = true;
|
|
3283
|
+
opts.enable_quarto_diagrams = true;
|
|
3284
|
+
opts.enable_quarto_shortcodes = true;
|
|
3285
|
+
opts.enable_quarto_xrefs = true;
|
|
3286
|
+
opts.enable_quarto_strict_lists = false;
|
|
3287
|
+
opts.enable_quarto_callouts = true;
|
|
3288
|
+
opts.enable_wiki_links = false;
|
|
3289
|
+
opts.enable_marked_extensions = false;
|
|
3290
|
+
opts.enable_py_callouts = false;
|
|
3291
|
+
opts.enable_indices = false;
|
|
3292
|
+
opts.enable_mmark_index_syntax = false;
|
|
3293
|
+
opts.enable_textindex_syntax = false;
|
|
3294
|
+
opts.enable_leanpub_index_syntax = false;
|
|
3295
|
+
break;
|
|
3141
3296
|
}
|
|
3142
3297
|
|
|
3143
3298
|
return opts;
|
|
3144
3299
|
}
|
|
3145
3300
|
|
|
3301
|
+
bool apex_options_apply_meta_file(apex_options *options, const char *path) {
|
|
3302
|
+
if (!options || !path || !path[0]) {
|
|
3303
|
+
return false;
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
apex_options original_options = *options;
|
|
3307
|
+
apex_metadata_item *metadata = apex_load_metadata_from_file(path);
|
|
3308
|
+
if (!metadata) {
|
|
3309
|
+
return false;
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
apex_apply_metadata_to_options(metadata, options);
|
|
3313
|
+
|
|
3314
|
+
bool owns_csl_file = options->csl_file &&
|
|
3315
|
+
options->csl_file != original_options.csl_file;
|
|
3316
|
+
bool owns_document_title = options->document_title &&
|
|
3317
|
+
options->document_title != original_options.document_title;
|
|
3318
|
+
bool owns_base_directory = options->base_directory &&
|
|
3319
|
+
options->base_directory != original_options.base_directory;
|
|
3320
|
+
bool owns_wikilink_extension = options->wikilink_extension &&
|
|
3321
|
+
options->wikilink_extension != original_options.wikilink_extension;
|
|
3322
|
+
bool owns_theme_name = options->theme_name &&
|
|
3323
|
+
options->theme_name != original_options.theme_name;
|
|
3324
|
+
bool owns_code_highlight_theme = options->code_highlight_theme &&
|
|
3325
|
+
options->code_highlight_theme != original_options.code_highlight_theme;
|
|
3326
|
+
bool owns_stylesheet_paths = options->stylesheet_paths &&
|
|
3327
|
+
options->stylesheet_paths != original_options.stylesheet_paths;
|
|
3328
|
+
|
|
3329
|
+
char *csl_file = owns_csl_file ? strdup(options->csl_file) : NULL;
|
|
3330
|
+
char *document_title = owns_document_title ? strdup(options->document_title) : NULL;
|
|
3331
|
+
char *base_directory = owns_base_directory ? strdup(options->base_directory) : NULL;
|
|
3332
|
+
char *wikilink_extension = owns_wikilink_extension ? strdup(options->wikilink_extension) : NULL;
|
|
3333
|
+
char *theme_name = owns_theme_name ? strdup(options->theme_name) : NULL;
|
|
3334
|
+
char *code_highlight_theme = owns_code_highlight_theme ? strdup(options->code_highlight_theme) : NULL;
|
|
3335
|
+
const char **stylesheet_paths = NULL;
|
|
3336
|
+
bool copy_failed = (owns_csl_file && !csl_file) ||
|
|
3337
|
+
(owns_document_title && !document_title) ||
|
|
3338
|
+
(owns_base_directory && !base_directory) ||
|
|
3339
|
+
(owns_wikilink_extension && !wikilink_extension) ||
|
|
3340
|
+
(owns_theme_name && !theme_name) ||
|
|
3341
|
+
(owns_code_highlight_theme && !code_highlight_theme);
|
|
3342
|
+
|
|
3343
|
+
if (owns_stylesheet_paths) {
|
|
3344
|
+
stylesheet_paths = calloc(options->stylesheet_count + 1, sizeof(*stylesheet_paths));
|
|
3345
|
+
if (!stylesheet_paths) {
|
|
3346
|
+
copy_failed = true;
|
|
3347
|
+
} else {
|
|
3348
|
+
for (size_t i = 0; i < options->stylesheet_count; i++) {
|
|
3349
|
+
if (options->stylesheet_paths[i]) {
|
|
3350
|
+
stylesheet_paths[i] = strdup(options->stylesheet_paths[i]);
|
|
3351
|
+
if (!stylesheet_paths[i]) {
|
|
3352
|
+
copy_failed = true;
|
|
3353
|
+
break;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
if (copy_failed) {
|
|
3361
|
+
free(csl_file);
|
|
3362
|
+
free(document_title);
|
|
3363
|
+
free(base_directory);
|
|
3364
|
+
free(wikilink_extension);
|
|
3365
|
+
free(theme_name);
|
|
3366
|
+
free(code_highlight_theme);
|
|
3367
|
+
if (stylesheet_paths) {
|
|
3368
|
+
for (size_t i = 0; i < options->stylesheet_count; i++) {
|
|
3369
|
+
free((void *)stylesheet_paths[i]);
|
|
3370
|
+
}
|
|
3371
|
+
free(stylesheet_paths);
|
|
3372
|
+
}
|
|
3373
|
+
if (owns_stylesheet_paths) {
|
|
3374
|
+
free(options->stylesheet_paths);
|
|
3375
|
+
}
|
|
3376
|
+
apex_free_metadata(metadata);
|
|
3377
|
+
*options = original_options;
|
|
3378
|
+
return false;
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
if (owns_stylesheet_paths) {
|
|
3382
|
+
free(options->stylesheet_paths);
|
|
3383
|
+
}
|
|
3384
|
+
apex_free_metadata(metadata);
|
|
3385
|
+
|
|
3386
|
+
if (owns_csl_file) options->csl_file = csl_file;
|
|
3387
|
+
if (owns_document_title) options->document_title = document_title;
|
|
3388
|
+
if (owns_base_directory) options->base_directory = base_directory;
|
|
3389
|
+
if (owns_wikilink_extension) options->wikilink_extension = wikilink_extension;
|
|
3390
|
+
if (owns_theme_name) options->theme_name = theme_name;
|
|
3391
|
+
if (owns_code_highlight_theme) options->code_highlight_theme = code_highlight_theme;
|
|
3392
|
+
if (owns_stylesheet_paths) options->stylesheet_paths = stylesheet_paths;
|
|
3393
|
+
return true;
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
static bool apex_quarto_feature(const apex_options *options, bool feature_enabled) {
|
|
3397
|
+
return feature_enabled &&
|
|
3398
|
+
(options->enable_quarto_extensions || options->mode == APEX_MODE_QUARTO);
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3146
3401
|
/**
|
|
3147
3402
|
* Convert cmark-gfm option flags based on Apex options
|
|
3148
3403
|
*/
|
|
@@ -3221,7 +3476,7 @@ static void apex_register_extensions(cmark_parser *parser, const apex_options *o
|
|
|
3221
3476
|
}
|
|
3222
3477
|
|
|
3223
3478
|
/* GFM autolink - enable for GFM and Unified modes if autolink is enabled */
|
|
3224
|
-
if (options->enable_autolink && (options->mode == APEX_MODE_GFM || options->mode
|
|
3479
|
+
if (options->enable_autolink && (options->mode == APEX_MODE_GFM || apex_mode_is_unified_family(options->mode))) {
|
|
3225
3480
|
cmark_syntax_extension *autolink_ext = cmark_find_syntax_extension("autolink");
|
|
3226
3481
|
if (autolink_ext) {
|
|
3227
3482
|
cmark_parser_attach_syntax_extension(parser, autolink_ext);
|
|
@@ -4479,6 +4734,30 @@ fail:
|
|
|
4479
4734
|
return NULL;
|
|
4480
4735
|
}
|
|
4481
4736
|
|
|
4737
|
+
apex_toc_entry *apex_markdown_to_toc_entries(const char *markdown, size_t len,
|
|
4738
|
+
const apex_options *options,
|
|
4739
|
+
size_t *out_count) {
|
|
4740
|
+
if (out_count) *out_count = 0;
|
|
4741
|
+
if (!out_count) return NULL;
|
|
4742
|
+
if (!markdown || len == 0) return NULL;
|
|
4743
|
+
|
|
4744
|
+
apex_options opts = options ? *options : apex_options_default();
|
|
4745
|
+
opts.output_format = APEX_OUTPUT_TOC;
|
|
4746
|
+
opts.toc_entries_out = NULL; /* set below after locals exist */
|
|
4747
|
+
opts.toc_entries_count_out = NULL;
|
|
4748
|
+
|
|
4749
|
+
apex_toc_entry *entries = NULL;
|
|
4750
|
+
size_t count = 0;
|
|
4751
|
+
opts.toc_entries_out = &entries;
|
|
4752
|
+
opts.toc_entries_count_out = &count;
|
|
4753
|
+
|
|
4754
|
+
char *discard = apex_markdown_to_html(markdown, len, &opts);
|
|
4755
|
+
apex_free_string(discard);
|
|
4756
|
+
|
|
4757
|
+
*out_count = count;
|
|
4758
|
+
return entries;
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4482
4761
|
char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options *options) {
|
|
4483
4762
|
if (!markdown || len == 0) {
|
|
4484
4763
|
char *empty = malloc(1);
|
|
@@ -4529,6 +4808,19 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4529
4808
|
plugin_manager = apex_plugins_load(options);
|
|
4530
4809
|
}
|
|
4531
4810
|
|
|
4811
|
+
char *quarto_shortcodes_processed = NULL;
|
|
4812
|
+
if (apex_quarto_feature(options, options->enable_quarto_shortcodes)) {
|
|
4813
|
+
bool warn_unknown = getenv("APEX_VERBOSE") != NULL;
|
|
4814
|
+
PROFILE_START(quarto_shortcodes_preprocess);
|
|
4815
|
+
quarto_shortcodes_processed = apex_preprocess_quarto_shortcodes(working_text, warn_unknown, options->unsafe);
|
|
4816
|
+
PROFILE_END(quarto_shortcodes_preprocess);
|
|
4817
|
+
if (quarto_shortcodes_processed) {
|
|
4818
|
+
free(working_text);
|
|
4819
|
+
working_text = quarto_shortcodes_processed;
|
|
4820
|
+
len = strlen(working_text);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4532
4824
|
/* Optional pre-parse plugin hook: run all configured pre_parse plugins
|
|
4533
4825
|
* over the raw markdown before any Apex-specific preprocessing.
|
|
4534
4826
|
*/
|
|
@@ -4552,6 +4844,19 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4552
4844
|
char **liquid_tags = NULL;
|
|
4553
4845
|
size_t liquid_tag_count = 0;
|
|
4554
4846
|
char *liquid_protected = NULL;
|
|
4847
|
+
char *py_callouts_processed = NULL;
|
|
4848
|
+
/* Process Python-Markdown/markdown-callouts syntax early so metadata parsing
|
|
4849
|
+
* doesn't consume top-of-file TYPE: lines before they can be treated as callouts.
|
|
4850
|
+
*/
|
|
4851
|
+
if (options->enable_py_callouts) {
|
|
4852
|
+
PROFILE_START(py_callouts_preprocess);
|
|
4853
|
+
py_callouts_processed = apex_preprocess_py_callouts(text_ptr);
|
|
4854
|
+
PROFILE_END(py_callouts_preprocess);
|
|
4855
|
+
if (py_callouts_processed) {
|
|
4856
|
+
text_ptr = py_callouts_processed;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
|
|
4555
4860
|
|
|
4556
4861
|
|
|
4557
4862
|
if (getenv("APEX_DEBUG_PIPELINE")) {
|
|
@@ -4565,7 +4870,7 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4565
4870
|
|
|
4566
4871
|
if (options->mode == APEX_MODE_MULTIMARKDOWN ||
|
|
4567
4872
|
options->mode == APEX_MODE_KRAMDOWN ||
|
|
4568
|
-
options->mode
|
|
4873
|
+
apex_mode_is_unified_family(options->mode)) {
|
|
4569
4874
|
/* Extract metadata FIRST */
|
|
4570
4875
|
PROFILE_START(metadata);
|
|
4571
4876
|
metadata = apex_extract_metadata_for_mode(&text_ptr, options->mode);
|
|
@@ -4577,7 +4882,7 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4577
4882
|
}
|
|
4578
4883
|
|
|
4579
4884
|
/* Extract ALDs for Kramdown */
|
|
4580
|
-
if (options->mode
|
|
4885
|
+
if (apex_mode_is_kramdown_or_unified_family(options->mode)) {
|
|
4581
4886
|
alds = apex_extract_alds(&text_ptr);
|
|
4582
4887
|
if (getenv("APEX_DEBUG_PIPELINE")) {
|
|
4583
4888
|
size_t len = strlen(text_ptr);
|
|
@@ -4717,13 +5022,27 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4717
5022
|
|
|
4718
5023
|
/* Process index entries (preprocessing) */
|
|
4719
5024
|
apex_index_registry index_registry = {0};
|
|
5025
|
+
char *concordance_processed = NULL;
|
|
4720
5026
|
char *indices_processed = NULL;
|
|
5027
|
+
if (options->enable_indices && options->concordance_files && options->concordance_files[0]) {
|
|
5028
|
+
PROGRESS_REPORT("Applying concordance", -1);
|
|
5029
|
+
PROFILE_START(concordance);
|
|
5030
|
+
concordance_processed = apex_apply_concordance(text_ptr, options);
|
|
5031
|
+
PROFILE_END(concordance);
|
|
5032
|
+
if (concordance_processed) {
|
|
5033
|
+
text_ptr = concordance_processed;
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
4721
5036
|
if (options->enable_indices) {
|
|
4722
5037
|
PROGRESS_REPORT("Processing indices", -1);
|
|
4723
5038
|
PROFILE_START(indices);
|
|
4724
5039
|
indices_processed = apex_process_index_entries(text_ptr, &index_registry, options);
|
|
4725
5040
|
PROFILE_END(indices);
|
|
4726
5041
|
if (indices_processed) {
|
|
5042
|
+
if (concordance_processed) {
|
|
5043
|
+
free(concordance_processed);
|
|
5044
|
+
concordance_processed = NULL;
|
|
5045
|
+
}
|
|
4727
5046
|
text_ptr = indices_processed;
|
|
4728
5047
|
}
|
|
4729
5048
|
}
|
|
@@ -4746,9 +5065,11 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4746
5065
|
/* Preprocess image attributes and URL-encode all link URLs */
|
|
4747
5066
|
image_attr_entry *img_attrs = NULL;
|
|
4748
5067
|
char *image_attrs_processed = NULL;
|
|
4749
|
-
if (options->mode
|
|
5068
|
+
if (apex_mode_is_unified_family(options->mode) ||
|
|
4750
5069
|
options->mode == APEX_MODE_MULTIMARKDOWN ||
|
|
4751
|
-
options->mode == APEX_MODE_KRAMDOWN
|
|
5070
|
+
options->mode == APEX_MODE_KRAMDOWN ||
|
|
5071
|
+
options->mode == APEX_MODE_GFM ||
|
|
5072
|
+
options->mode == APEX_MODE_COMMONMARK) {
|
|
4752
5073
|
PROFILE_START(image_attrs_preprocess);
|
|
4753
5074
|
image_attrs_processed = apex_preprocess_image_attributes(text_ptr, &img_attrs, options->mode);
|
|
4754
5075
|
PROFILE_END(image_attrs_preprocess);
|
|
@@ -4764,7 +5085,8 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4764
5085
|
|
|
4765
5086
|
/* Preprocess IAL markers (insert blank lines before them so cmark parses correctly) */
|
|
4766
5087
|
char *ial_preprocessed = NULL;
|
|
4767
|
-
|
|
5088
|
+
char *escaped_toc_protected = NULL;
|
|
5089
|
+
if (options->mode == APEX_MODE_KRAMDOWN || apex_mode_is_unified_family(options->mode)) {
|
|
4768
5090
|
PROFILE_START(ial_preprocess);
|
|
4769
5091
|
ial_preprocessed = apex_preprocess_ial(text_ptr);
|
|
4770
5092
|
PROFILE_END(ial_preprocess);
|
|
@@ -4778,9 +5100,42 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4778
5100
|
}
|
|
4779
5101
|
}
|
|
4780
5102
|
|
|
5103
|
+
/* Preprocess grid tables BEFORE spans/special_markers.
|
|
5104
|
+
* Grid tables must process === separators before proofreader/highlight converts them.
|
|
5105
|
+
* Converts Pandoc +---+ syntax to pipe tables for the normal table pipeline.
|
|
5106
|
+
*/
|
|
5107
|
+
char *grid_tables_processed = NULL;
|
|
5108
|
+
char *normalized_for_grid_tables = NULL;
|
|
5109
|
+
if (options->enable_grid_tables && options->enable_tables) {
|
|
5110
|
+
size_t pre_grid_len = strlen(text_ptr);
|
|
5111
|
+
bool needs_newline_for_grid = (pre_grid_len > 0 &&
|
|
5112
|
+
text_ptr[pre_grid_len - 1] != '\n' &&
|
|
5113
|
+
text_ptr[pre_grid_len - 1] != '\r');
|
|
5114
|
+
if (needs_newline_for_grid) {
|
|
5115
|
+
normalized_for_grid_tables = malloc(pre_grid_len + 2);
|
|
5116
|
+
if (normalized_for_grid_tables) {
|
|
5117
|
+
memcpy(normalized_for_grid_tables, text_ptr, pre_grid_len);
|
|
5118
|
+
normalized_for_grid_tables[pre_grid_len] = '\n';
|
|
5119
|
+
normalized_for_grid_tables[pre_grid_len + 1] = '\0';
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5123
|
+
PROFILE_START(grid_tables_preprocess);
|
|
5124
|
+
grid_tables_processed = apex_preprocess_grid_tables(normalized_for_grid_tables ? normalized_for_grid_tables : text_ptr);
|
|
5125
|
+
PROFILE_END(grid_tables_preprocess);
|
|
5126
|
+
|
|
5127
|
+
if (normalized_for_grid_tables) {
|
|
5128
|
+
free(normalized_for_grid_tables);
|
|
5129
|
+
}
|
|
5130
|
+
|
|
5131
|
+
if (grid_tables_processed) {
|
|
5132
|
+
text_ptr = grid_tables_processed;
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
|
|
4781
5136
|
/* Preprocess bracketed spans [text]{IAL} */
|
|
4782
5137
|
char *spans_preprocessed = NULL;
|
|
4783
|
-
if (options->enable_spans && (options->mode
|
|
5138
|
+
if (options->enable_spans && apex_mode_is_kramdown_or_unified_family(options->mode)) {
|
|
4784
5139
|
PROFILE_START(spans_preprocess);
|
|
4785
5140
|
spans_preprocessed = apex_preprocess_bracketed_spans(text_ptr);
|
|
4786
5141
|
PROFILE_END(spans_preprocess);
|
|
@@ -4793,7 +5148,13 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4793
5148
|
char *includes_processed = NULL;
|
|
4794
5149
|
if (options->enable_file_includes) {
|
|
4795
5150
|
PROFILE_START(includes);
|
|
4796
|
-
includes_processed = apex_process_includes(text_ptr,
|
|
5151
|
+
includes_processed = apex_process_includes(text_ptr,
|
|
5152
|
+
options->base_directory,
|
|
5153
|
+
metadata,
|
|
5154
|
+
0,
|
|
5155
|
+
options->wikilink_extension,
|
|
5156
|
+
plugin_manager,
|
|
5157
|
+
options);
|
|
4797
5158
|
PROFILE_END(includes);
|
|
4798
5159
|
if (includes_processed) {
|
|
4799
5160
|
text_ptr = includes_processed;
|
|
@@ -4821,11 +5182,58 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4821
5182
|
text_ptr = inline_tables_processed;
|
|
4822
5183
|
}
|
|
4823
5184
|
|
|
5185
|
+
/* Pandoc/Quarto list extensions before alpha/roman marker normalization */
|
|
5186
|
+
char *example_lists_processed = NULL;
|
|
5187
|
+
char *line_blocks_processed = NULL;
|
|
5188
|
+
char *roman_lists_processed = NULL;
|
|
5189
|
+
char *strict_lists_processed = NULL;
|
|
5190
|
+
if (apex_quarto_feature(options, options->enable_quarto_example_lists)) {
|
|
5191
|
+
PROFILE_START(example_lists_preprocess);
|
|
5192
|
+
example_lists_processed = apex_preprocess_example_lists(text_ptr);
|
|
5193
|
+
PROFILE_END(example_lists_preprocess);
|
|
5194
|
+
if (example_lists_processed) {
|
|
5195
|
+
text_ptr = example_lists_processed;
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
|
|
5199
|
+
if (apex_quarto_feature(options, options->enable_quarto_line_blocks)) {
|
|
5200
|
+
PROFILE_START(line_blocks_preprocess);
|
|
5201
|
+
line_blocks_processed = apex_preprocess_line_blocks(text_ptr, options->unsafe);
|
|
5202
|
+
PROFILE_END(line_blocks_preprocess);
|
|
5203
|
+
if (line_blocks_processed) {
|
|
5204
|
+
text_ptr = line_blocks_processed;
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
|
|
5208
|
+
if (apex_quarto_feature(options, options->enable_quarto_roman_lists)) {
|
|
5209
|
+
PROFILE_START(roman_lists_preprocess);
|
|
5210
|
+
roman_lists_processed = apex_preprocess_roman_lists(text_ptr);
|
|
5211
|
+
PROFILE_END(roman_lists_preprocess);
|
|
5212
|
+
if (roman_lists_processed) {
|
|
5213
|
+
text_ptr = roman_lists_processed;
|
|
5214
|
+
}
|
|
5215
|
+
}
|
|
5216
|
+
|
|
5217
|
+
if (apex_quarto_feature(options, options->enable_quarto_strict_lists)) {
|
|
5218
|
+
PROFILE_START(quarto_strict_lists_preprocess);
|
|
5219
|
+
strict_lists_processed = apex_preprocess_quarto_strict_lists(text_ptr);
|
|
5220
|
+
PROFILE_END(quarto_strict_lists_preprocess);
|
|
5221
|
+
if (strict_lists_processed) {
|
|
5222
|
+
text_ptr = strict_lists_processed;
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
|
|
4824
5226
|
/* Process alpha lists before parsing (preprocessing) */
|
|
4825
5227
|
char *alpha_lists_processed = NULL;
|
|
5228
|
+
bool inserted_synthetic_nested_alpha_break = false;
|
|
5229
|
+
bool saw_explicit_nested_alpha_break = false;
|
|
4826
5230
|
if (options->allow_alpha_lists) {
|
|
4827
5231
|
PROFILE_START(alpha_lists);
|
|
4828
|
-
alpha_lists_processed = apex_preprocess_alpha_lists(
|
|
5232
|
+
alpha_lists_processed = apex_preprocess_alpha_lists(
|
|
5233
|
+
text_ptr,
|
|
5234
|
+
&inserted_synthetic_nested_alpha_break,
|
|
5235
|
+
&saw_explicit_nested_alpha_break
|
|
5236
|
+
);
|
|
4829
5237
|
PROFILE_END(alpha_lists);
|
|
4830
5238
|
if (alpha_lists_processed) {
|
|
4831
5239
|
text_ptr = alpha_lists_processed;
|
|
@@ -4833,10 +5241,12 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
4833
5241
|
}
|
|
4834
5242
|
|
|
4835
5243
|
char *nested_ordered_sublists_processed = NULL;
|
|
5244
|
+
bool inserted_synthetic_nested_ordered_break = false;
|
|
5245
|
+
bool saw_explicit_nested_ordered_break = false;
|
|
4836
5246
|
|
|
4837
5247
|
/* Process emoji autocorrect before parsing (preprocessing) */
|
|
4838
5248
|
char *emoji_autocorrect_processed = NULL;
|
|
4839
|
-
if (options->enable_emoji_autocorrect && (options->mode ==
|
|
5249
|
+
if (options->enable_emoji_autocorrect && (options->mode == APEX_MODE_GFM || apex_mode_is_unified_family(options->mode))) {
|
|
4840
5250
|
PROFILE_START(emoji_autocorrect);
|
|
4841
5251
|
emoji_autocorrect_processed = apex_autocorrect_emoji_names(text_ptr);
|
|
4842
5252
|
PROFILE_END(emoji_autocorrect);
|
|
@@ -5063,10 +5473,52 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5063
5473
|
}
|
|
5064
5474
|
}
|
|
5065
5475
|
|
|
5476
|
+
/* Pandoc/Quarto raw content ({=format}) before other fence preprocessors */
|
|
5477
|
+
char *raw_content_processed = NULL;
|
|
5478
|
+
char *code_fence_attrs_processed = NULL;
|
|
5479
|
+
char *quarto_diagrams_processed = NULL;
|
|
5480
|
+
if (apex_quarto_feature(options, options->enable_quarto_raw)) {
|
|
5481
|
+
PROFILE_START(raw_content_preprocess);
|
|
5482
|
+
raw_content_processed = apex_preprocess_raw_content(text_ptr, options->unsafe);
|
|
5483
|
+
PROFILE_END(raw_content_preprocess);
|
|
5484
|
+
if (raw_content_processed) {
|
|
5485
|
+
text_ptr = raw_content_processed;
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5489
|
+
if (apex_quarto_feature(options, options->enable_quarto_code_attrs)) {
|
|
5490
|
+
PROFILE_START(code_fence_attrs_preprocess);
|
|
5491
|
+
code_fence_attrs_processed = apex_preprocess_code_fence_attrs(text_ptr);
|
|
5492
|
+
PROFILE_END(code_fence_attrs_preprocess);
|
|
5493
|
+
if (code_fence_attrs_processed) {
|
|
5494
|
+
text_ptr = code_fence_attrs_processed;
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
|
|
5498
|
+
if (apex_quarto_feature(options, options->enable_quarto_diagrams)) {
|
|
5499
|
+
PROFILE_START(quarto_diagrams_preprocess);
|
|
5500
|
+
quarto_diagrams_processed = apex_preprocess_quarto_diagrams(text_ptr, options->unsafe);
|
|
5501
|
+
PROFILE_END(quarto_diagrams_preprocess);
|
|
5502
|
+
if (quarto_diagrams_processed) {
|
|
5503
|
+
text_ptr = quarto_diagrams_processed;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5507
|
+
/* Process Quarto ::: callouts before fenced divs so recognized callouts bypass generic div conversion */
|
|
5508
|
+
char *quarto_callouts_processed = NULL;
|
|
5509
|
+
if (options->enable_quarto_callouts) {
|
|
5510
|
+
PROFILE_START(quarto_callouts_preprocess);
|
|
5511
|
+
quarto_callouts_processed = apex_preprocess_quarto_callouts(text_ptr);
|
|
5512
|
+
PROFILE_END(quarto_callouts_preprocess);
|
|
5513
|
+
if (quarto_callouts_processed) {
|
|
5514
|
+
text_ptr = quarto_callouts_processed;
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
|
|
5066
5518
|
/* Process fenced divs before parsing (preprocessing) */
|
|
5067
5519
|
/* Only enabled in Unified mode */
|
|
5068
5520
|
char *fenced_divs_processed = NULL;
|
|
5069
|
-
if (options->enable_divs && options->mode
|
|
5521
|
+
if (options->enable_divs && apex_mode_is_unified_family(options->mode)) {
|
|
5070
5522
|
PROFILE_START(fenced_divs);
|
|
5071
5523
|
fenced_divs_processed = apex_process_fenced_divs(text_ptr);
|
|
5072
5524
|
PROFILE_END(fenced_divs);
|
|
@@ -5473,9 +5925,13 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5473
5925
|
}
|
|
5474
5926
|
|
|
5475
5927
|
/* Keep this late so later preprocessors do not collapse inserted separation. */
|
|
5476
|
-
if (options->mode
|
|
5928
|
+
if (apex_mode_is_unified_family(options->mode)) {
|
|
5477
5929
|
PROFILE_START(nested_ordered_sublists);
|
|
5478
|
-
nested_ordered_sublists_processed = apex_preprocess_nested_ordered_sublists(
|
|
5930
|
+
nested_ordered_sublists_processed = apex_preprocess_nested_ordered_sublists(
|
|
5931
|
+
text_ptr,
|
|
5932
|
+
&inserted_synthetic_nested_ordered_break,
|
|
5933
|
+
&saw_explicit_nested_ordered_break
|
|
5934
|
+
);
|
|
5479
5935
|
PROFILE_END(nested_ordered_sublists);
|
|
5480
5936
|
if (nested_ordered_sublists_processed) {
|
|
5481
5937
|
text_ptr = nested_ordered_sublists_processed;
|
|
@@ -5526,6 +5982,15 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5526
5982
|
/* Convert options to cmark-gfm format */
|
|
5527
5983
|
int cmark_opts = apex_to_cmark_options(options);
|
|
5528
5984
|
|
|
5985
|
+
/* Protect backslash-escaped {{TOC...}} markers before parsing */
|
|
5986
|
+
if (options->enable_marked_extensions || options->mode == APEX_MODE_MULTIMARKDOWN) {
|
|
5987
|
+
escaped_toc_protected = apex_protect_escaped_toc_markers(text_ptr);
|
|
5988
|
+
if (escaped_toc_protected) {
|
|
5989
|
+
text_ptr = escaped_toc_protected;
|
|
5990
|
+
text_len = strlen(text_ptr);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
|
|
5529
5994
|
/* Create parser */
|
|
5530
5995
|
PROFILE_START(parsing);
|
|
5531
5996
|
cmark_parser *parser = cmark_parser_new(cmark_opts);
|
|
@@ -5596,6 +6061,8 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5596
6061
|
} else if (options->output_format == APEX_OUTPUT_TERMINAL ||
|
|
5597
6062
|
options->output_format == APEX_OUTPUT_TERMINAL256) {
|
|
5598
6063
|
target_format = "terminal";
|
|
6064
|
+
} else if (options->output_format == APEX_OUTPUT_RTF) {
|
|
6065
|
+
target_format = "rtf";
|
|
5599
6066
|
}
|
|
5600
6067
|
cmark_node *filtered = apex_run_ast_filters(document, options, target_format);
|
|
5601
6068
|
if (!filtered && options->ast_filter_strict) {
|
|
@@ -5631,14 +6098,14 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5631
6098
|
|
|
5632
6099
|
/* Postprocess callouts if enabled */
|
|
5633
6100
|
if (options->enable_callouts) {
|
|
5634
|
-
apex_process_callouts_in_tree(document);
|
|
6101
|
+
apex_process_callouts_in_tree(document, options->enable_py_callouts);
|
|
5635
6102
|
}
|
|
5636
6103
|
|
|
5637
6104
|
/* Process IAL (Inline Attribute Lists) BEFORE manual header IDs.
|
|
5638
6105
|
IAL handles {: #id}, {#id}, and {.class} - running first ensures these
|
|
5639
6106
|
are extracted and removed from heading text before manual header ID
|
|
5640
6107
|
looks for MMD [id] or Kramdown {#id}. Avoids duplicate handling. */
|
|
5641
|
-
if (alds || options->mode
|
|
6108
|
+
if (alds || apex_mode_is_kramdown_or_unified_family(options->mode)) {
|
|
5642
6109
|
/* Fast path: skip AST walk if no IAL markers present */
|
|
5643
6110
|
/* Check for both Kramdown-style ({:) and Pandoc-style ({# or {.) IALs */
|
|
5644
6111
|
if (strstr(text_ptr, "{:") != NULL ||
|
|
@@ -5676,6 +6143,11 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5676
6143
|
if (options->allow_mixed_list_markers) {
|
|
5677
6144
|
apex_merge_mixed_list_markers(document);
|
|
5678
6145
|
}
|
|
6146
|
+
if (apex_mode_is_unified_family(options->mode) &&
|
|
6147
|
+
((inserted_synthetic_nested_ordered_break && !saw_explicit_nested_ordered_break) ||
|
|
6148
|
+
(inserted_synthetic_nested_alpha_break && !saw_explicit_nested_alpha_break))) {
|
|
6149
|
+
apex_tighten_nested_ordered_list_items(document);
|
|
6150
|
+
}
|
|
5679
6151
|
|
|
5680
6152
|
/* Note: Critic Markup is now handled via preprocessing (before parsing) */
|
|
5681
6153
|
|
|
@@ -5687,6 +6159,18 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5687
6159
|
return json;
|
|
5688
6160
|
}
|
|
5689
6161
|
|
|
6162
|
+
if (options->output_format == APEX_OUTPUT_TOC) {
|
|
6163
|
+
if (options->toc_entries_out && options->toc_entries_count_out) {
|
|
6164
|
+
*options->toc_entries_out = apex_generate_toc_entries(
|
|
6165
|
+
document, options->id_format, options->toc_min, options->toc_max,
|
|
6166
|
+
options->toc_entries_count_out);
|
|
6167
|
+
return strdup("");
|
|
6168
|
+
}
|
|
6169
|
+
char *toc_md = apex_generate_toc_markdown(document, options->id_format,
|
|
6170
|
+
options->toc_min, options->toc_max);
|
|
6171
|
+
return toc_md ? toc_md : strdup("");
|
|
6172
|
+
}
|
|
6173
|
+
|
|
5690
6174
|
/* If output format is Markdown, serialize AST to Markdown and return */
|
|
5691
6175
|
if (options->output_format == APEX_OUTPUT_MARKDOWN ||
|
|
5692
6176
|
options->output_format == APEX_OUTPUT_MMD ||
|
|
@@ -5729,13 +6213,19 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5729
6213
|
return man_html ? man_html : strdup("<!DOCTYPE html><html><body><p>stub</p></body></html>");
|
|
5730
6214
|
}
|
|
5731
6215
|
|
|
6216
|
+
/* If output format is RTF, serialize AST and return */
|
|
6217
|
+
if (options->output_format == APEX_OUTPUT_RTF) {
|
|
6218
|
+
char *rtf = apex_cmark_to_rtf(document, options);
|
|
6219
|
+
return rtf ? rtf : strdup("{\\rtf1\\ansi\\deff0\\pard\\par}\n");
|
|
6220
|
+
}
|
|
6221
|
+
|
|
5732
6222
|
/* Render to HTML
|
|
5733
6223
|
* Use custom renderer when we have attributes (IAL, ALDs, or image attributes)
|
|
5734
6224
|
* Otherwise use standard renderer
|
|
5735
6225
|
*/
|
|
5736
6226
|
PROFILE_START(rendering);
|
|
5737
6227
|
char *html;
|
|
5738
|
-
if (img_attrs || alds || options->mode
|
|
6228
|
+
if (img_attrs || alds || apex_mode_is_kramdown_or_unified_family(options->mode)) {
|
|
5739
6229
|
/* Use custom renderer to inject attributes */
|
|
5740
6230
|
html = apex_render_html_with_attributes(document, cmark_opts);
|
|
5741
6231
|
} else {
|
|
@@ -5761,7 +6251,7 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
5761
6251
|
}
|
|
5762
6252
|
|
|
5763
6253
|
/* Restore custom element tags from fenced-div wrappers (fixes out-of-order HTML) */
|
|
5764
|
-
if (html && options->enable_divs && options->mode
|
|
6254
|
+
if (html && options->enable_divs && apex_mode_is_unified_family(options->mode)) {
|
|
5765
6255
|
char *fenced_divs_html = apex_postprocess_fenced_divs_html(html);
|
|
5766
6256
|
if (fenced_divs_html) {
|
|
5767
6257
|
free(html);
|
|
@@ -6041,12 +6531,18 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6041
6531
|
*/
|
|
6042
6532
|
if ((options->enable_marked_extensions || options->mode == APEX_MODE_MULTIMARKDOWN) && html) {
|
|
6043
6533
|
PROFILE_START(toc);
|
|
6044
|
-
char *with_toc = apex_process_toc(html, document, options->id_format
|
|
6534
|
+
char *with_toc = apex_process_toc(html, document, options->id_format,
|
|
6535
|
+
options->toc_min, options->toc_max);
|
|
6045
6536
|
PROFILE_END(toc);
|
|
6046
6537
|
if (with_toc) {
|
|
6047
6538
|
free(html);
|
|
6048
6539
|
html = with_toc;
|
|
6049
6540
|
}
|
|
6541
|
+
char *restored = apex_restore_escaped_toc_markers(html);
|
|
6542
|
+
if (restored) {
|
|
6543
|
+
free(html);
|
|
6544
|
+
html = restored;
|
|
6545
|
+
}
|
|
6050
6546
|
}
|
|
6051
6547
|
|
|
6052
6548
|
/* Apply ARIA labels if enabled */
|
|
@@ -6077,6 +6573,16 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6077
6573
|
}
|
|
6078
6574
|
}
|
|
6079
6575
|
|
|
6576
|
+
if (apex_quarto_feature(options, options->enable_quarto_code_attrs) && html) {
|
|
6577
|
+
PROFILE_START(code_fence_attrs_postprocess);
|
|
6578
|
+
char *fence_html = apex_postprocess_code_fence_attrs_html(html);
|
|
6579
|
+
PROFILE_END(code_fence_attrs_postprocess);
|
|
6580
|
+
if (fence_html) {
|
|
6581
|
+
free(html);
|
|
6582
|
+
html = fence_html;
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
|
|
6080
6586
|
/* Replace abbreviations if any were found */
|
|
6081
6587
|
if (abbreviations && html) {
|
|
6082
6588
|
PROFILE_START(abbreviations);
|
|
@@ -6089,7 +6595,7 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6089
6595
|
}
|
|
6090
6596
|
|
|
6091
6597
|
/* Replace GitHub emoji if in GFM or Unified mode */
|
|
6092
|
-
if ((options->mode == APEX_MODE_GFM || options->mode
|
|
6598
|
+
if ((options->mode == APEX_MODE_GFM || apex_mode_is_unified_family(options->mode)) && html) {
|
|
6093
6599
|
PROFILE_START(emoji);
|
|
6094
6600
|
char *with_emoji = apex_replace_emoji(html);
|
|
6095
6601
|
PROFILE_END(emoji);
|
|
@@ -6211,6 +6717,26 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6211
6717
|
}
|
|
6212
6718
|
}
|
|
6213
6719
|
|
|
6720
|
+
if (apex_quarto_feature(options, options->enable_quarto_roman_lists) && html) {
|
|
6721
|
+
PROFILE_START(roman_lists_postprocess);
|
|
6722
|
+
char *roman_html = apex_postprocess_roman_lists_html(html);
|
|
6723
|
+
PROFILE_END(roman_lists_postprocess);
|
|
6724
|
+
if (roman_html) {
|
|
6725
|
+
free(html);
|
|
6726
|
+
html = roman_html;
|
|
6727
|
+
}
|
|
6728
|
+
}
|
|
6729
|
+
|
|
6730
|
+
if (apex_quarto_feature(options, options->enable_quarto_xrefs) && html) {
|
|
6731
|
+
PROFILE_START(quarto_xrefs_postprocess);
|
|
6732
|
+
char *xref_html = apex_postprocess_quarto_xrefs_html(html);
|
|
6733
|
+
PROFILE_END(quarto_xrefs_postprocess);
|
|
6734
|
+
if (xref_html) {
|
|
6735
|
+
free(html);
|
|
6736
|
+
html = xref_html;
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6214
6740
|
/* Remove empty paragraphs created by ^ marker (zero-width space only) */
|
|
6215
6741
|
if (html && options->enable_marked_extensions) {
|
|
6216
6742
|
PROFILE_START(remove_empty_paragraphs);
|
|
@@ -6230,7 +6756,18 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6230
6756
|
cmark_parser_free(parser);
|
|
6231
6757
|
free(working_text);
|
|
6232
6758
|
if (ial_preprocessed) free(ial_preprocessed);
|
|
6759
|
+
if (escaped_toc_protected) free(escaped_toc_protected);
|
|
6233
6760
|
if (spans_preprocessed) free(spans_preprocessed);
|
|
6761
|
+
if (grid_tables_processed) free(grid_tables_processed);
|
|
6762
|
+
if (raw_content_processed) free(raw_content_processed);
|
|
6763
|
+
if (code_fence_attrs_processed) free(code_fence_attrs_processed);
|
|
6764
|
+
if (quarto_diagrams_processed) free(quarto_diagrams_processed);
|
|
6765
|
+
if (example_lists_processed) free(example_lists_processed);
|
|
6766
|
+
if (line_blocks_processed) free(line_blocks_processed);
|
|
6767
|
+
if (roman_lists_processed) free(roman_lists_processed);
|
|
6768
|
+
if (strict_lists_processed) free(strict_lists_processed);
|
|
6769
|
+
if (quarto_callouts_processed) free(quarto_callouts_processed);
|
|
6770
|
+
if (py_callouts_processed) free(py_callouts_processed);
|
|
6234
6771
|
if (includes_processed) free(includes_processed);
|
|
6235
6772
|
if (emoji_autocorrect_processed) free(emoji_autocorrect_processed);
|
|
6236
6773
|
if (markers_processed_early) {
|
|
@@ -6250,6 +6787,7 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6250
6787
|
if (deflist_processed) free(deflist_processed);
|
|
6251
6788
|
if (fenced_divs_processed) free(fenced_divs_processed);
|
|
6252
6789
|
if (metadata_replaced) free(metadata_replaced);
|
|
6790
|
+
if (concordance_processed) free(concordance_processed);
|
|
6253
6791
|
if (autolinks_processed) free(autolinks_processed);
|
|
6254
6792
|
if (html_markdown_processed) free(html_markdown_processed);
|
|
6255
6793
|
if (hashtags_processed) free(hashtags_processed);
|
|
@@ -6284,28 +6822,55 @@ char *apex_markdown_to_html(const char *markdown, size_t len, const apex_options
|
|
|
6284
6822
|
|
|
6285
6823
|
/* Build script HTML (if any) from script_tags before wrapping or appending */
|
|
6286
6824
|
char *scripts_html = NULL;
|
|
6287
|
-
|
|
6825
|
+
const char *auto_mermaid_script = NULL;
|
|
6826
|
+
if (local_opts.standalone && html && local_opts.enable_quarto_diagrams &&
|
|
6827
|
+
apex_html_has_mermaid_diagram(html)) {
|
|
6828
|
+
bool has_mermaid_script = false;
|
|
6829
|
+
if (local_opts.script_tags) {
|
|
6830
|
+
for (char **p = local_opts.script_tags; *p; ++p) {
|
|
6831
|
+
if (*p && strstr(*p, "mermaid")) {
|
|
6832
|
+
has_mermaid_script = true;
|
|
6833
|
+
break;
|
|
6834
|
+
}
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
if (!has_mermaid_script) {
|
|
6838
|
+
auto_mermaid_script = apex_mermaid_script_tag();
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
|
|
6842
|
+
if (local_opts.script_tags || auto_mermaid_script) {
|
|
6288
6843
|
/* Join script tag snippets with newlines */
|
|
6289
6844
|
size_t total_len = 0;
|
|
6290
6845
|
size_t count = 0;
|
|
6291
|
-
for (char **p = local_opts.script_tags; *p; ++p) {
|
|
6846
|
+
for (char **p = local_opts.script_tags; p && *p; ++p) {
|
|
6292
6847
|
size_t len = strlen(*p);
|
|
6293
6848
|
if (len == 0) continue;
|
|
6294
6849
|
total_len += len + 1; /* +1 for newline */
|
|
6295
6850
|
count++;
|
|
6296
6851
|
}
|
|
6852
|
+
if (auto_mermaid_script) {
|
|
6853
|
+
total_len += strlen(auto_mermaid_script) + 1;
|
|
6854
|
+
count++;
|
|
6855
|
+
}
|
|
6297
6856
|
|
|
6298
6857
|
if (count > 0 && total_len > 0) {
|
|
6299
6858
|
scripts_html = malloc(total_len + 1); /* +1 for null terminator */
|
|
6300
6859
|
if (scripts_html) {
|
|
6301
6860
|
size_t pos = 0;
|
|
6302
|
-
for (char **p = local_opts.script_tags; *p; ++p) {
|
|
6861
|
+
for (char **p = local_opts.script_tags; p && *p; ++p) {
|
|
6303
6862
|
size_t len = strlen(*p);
|
|
6304
6863
|
if (len == 0) continue;
|
|
6305
6864
|
memcpy(scripts_html + pos, *p, len);
|
|
6306
6865
|
pos += len;
|
|
6307
6866
|
scripts_html[pos++] = '\n';
|
|
6308
6867
|
}
|
|
6868
|
+
if (auto_mermaid_script) {
|
|
6869
|
+
size_t len = strlen(auto_mermaid_script);
|
|
6870
|
+
memcpy(scripts_html + pos, auto_mermaid_script, len);
|
|
6871
|
+
pos += len;
|
|
6872
|
+
scripts_html[pos++] = '\n';
|
|
6873
|
+
}
|
|
6309
6874
|
scripts_html[pos] = '\0';
|
|
6310
6875
|
}
|
|
6311
6876
|
}
|
|
@@ -7016,6 +7581,11 @@ char *apex_wrap_html_document(const char *content, const char *title, const char
|
|
|
7016
7581
|
" ins { background: #d4fcbc; text-decoration: none; }\n"
|
|
7017
7582
|
" del { background: #fbb6c2; text-decoration: line-through; }\n"
|
|
7018
7583
|
" mark { background: #fff3cd; }\n"
|
|
7584
|
+
" .smallcaps { font-variant: small-caps; }\n"
|
|
7585
|
+
" .underline { text-decoration: underline; }\n"
|
|
7586
|
+
" span.mark { background: #fff3cd; }\n"
|
|
7587
|
+
" .hidden { display: none; }\n"
|
|
7588
|
+
" .quarto-xref { font-variant: normal; }\n"
|
|
7019
7589
|
" .critic.comment { background: #e7e7e7; color: #666; font-style: italic; }\n"
|
|
7020
7590
|
" .mkhashtag { color: #666; }\n"
|
|
7021
7591
|
" .mkstyledtag {\n"
|