apex-ruby 1.0.29 → 1.0.31
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 +2 -8
- data/apex-ruby.gemspec +14 -3
- data/ext/apex_ext/apex_src/include/apex/apex.h +11 -2
- data/ext/apex_ext/apex_src/src/apex.c +64 -5
- data/ext/apex_ext/apex_src/src/ast_terminal.c +2 -4
- data/ext/apex_ext/apex_src/src/extensions/callouts.c +103 -63
- data/ext/apex_ext/apex_src/src/extensions/callouts.h +3 -2
- data/ext/apex_ext/apex_src/src/extensions/citations.c +15 -2
- data/ext/apex_ext/apex_src/src/extensions/emoji.c +73 -7
- data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +25 -4
- data/ext/apex_ext/apex_src/src/extensions/html_markdown.h +4 -1
- data/ext/apex_ext/apex_src/src/extensions/insert.c +49 -0
- data/ext/apex_ext/apex_src/src/extensions/metadata.c +275 -55
- data/ext/apex_ext/apex_src/src/extensions/metadata.h +9 -0
- data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +172 -254
- data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +36 -127
- data/ext/apex_ext/apex_src/src/html_renderer.c +99 -157
- data/ext/apex_ext/extconf.rb +31 -79
- data/lib/apex/version.rb +1 -1
- metadata +1 -470
- data/ext/apex_ext/apex_src/AGENTS.md +0 -41
- data/ext/apex_ext/apex_src/BENCHMARK.md +0 -32
- data/ext/apex_ext/apex_src/BENCHMARK_COMPARISON.md +0 -67
- data/ext/apex_ext/apex_src/CHANGELOG.md +0 -3373
- data/ext/apex_ext/apex_src/CMakeLists.txt +0 -481
- data/ext/apex_ext/apex_src/Dockerfile.linux-build +0 -15
- data/ext/apex_ext/apex_src/Formula/apex.rb +0 -43
- data/ext/apex_ext/apex_src/Info.plist.in +0 -27
- data/ext/apex_ext/apex_src/Package.swift +0 -192
- data/ext/apex_ext/apex_src/PackageSupport/README.md +0 -17
- data/ext/apex_ext/apex_src/PackageSupport/cmark-gfm/cmark_gfm_spm_stub.c +0 -4
- data/ext/apex_ext/apex_src/README.md +0 -536
- data/ext/apex_ext/apex_src/ROADMAP.md +0 -5
- data/ext/apex_ext/apex_src/VERSION +0 -1
- data/ext/apex_ext/apex_src/apex-debug.log +0 -4
- data/ext/apex_ext/apex_src/apex-header-2-rb@2x.webp +0 -0
- data/ext/apex_ext/apex_src/apex-plugins.json.example +0 -20
- data/ext/apex_ext/apex_src/apex.code-workspace +0 -38
- data/ext/apex_ext/apex_src/apex.pc.in +0 -11
- data/ext/apex_ext/apex_src/apex.taskpaper +0 -12
- data/ext/apex_ext/apex_src/apex_deflist_debug.log +0 -25
- data/ext/apex_ext/apex_src/buildnotes.md +0 -1435
- data/ext/apex_ext/apex_src/cli/main.c +0 -4588
- data/ext/apex_ext/apex_src/debug.log +0 -7
- data/ext/apex_ext/apex_src/debug_test.sh +0 -22
- data/ext/apex_ext/apex_src/docs/API_REFERENCE.md +0 -451
- data/ext/apex_ext/apex_src/docs/ARCHITECTURE.md +0 -166
- data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +0 -274
- data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +0 -749
- data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +0 -744
- data/ext/apex_ext/apex_src/docs/CMARK_INTEGRATION.md +0 -220
- data/ext/apex_ext/apex_src/docs/CRITICMARKUP.md +0 -501
- data/ext/apex_ext/apex_src/docs/DEBUGGING.md +0 -73
- data/ext/apex_ext/apex_src/docs/FINAL_STATUS.md +0 -391
- data/ext/apex_ext/apex_src/docs/FINAL_STATUS_UPDATE.md +0 -237
- data/ext/apex_ext/apex_src/docs/IAL_FEATURES.md +0 -210
- data/ext/apex_ext/apex_src/docs/IAL_STATUS.md +0 -344
- data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +0 -382
- data/ext/apex_ext/apex_src/docs/INTEGRATION_EXAMPLE.m +0 -144
- data/ext/apex_ext/apex_src/docs/LIMITATIONS_RESOLVED.md +0 -278
- data/ext/apex_ext/apex_src/docs/OUTPUT_MODES.md +0 -321
- data/ext/apex_ext/apex_src/docs/PROGRESS.md +0 -167
- data/ext/apex_ext/apex_src/docs/STANDALONE_FEATURE.md +0 -174
- data/ext/apex_ext/apex_src/docs/TABLE_SPANS_STATUS.md +0 -243
- data/ext/apex_ext/apex_src/docs/TEST_COVERAGE.md +0 -316
- data/ext/apex_ext/apex_src/docs/USER_GUIDE.md +0 -803
- data/ext/apex_ext/apex_src/docs/WIKI_LINKS_ISSUE.md +0 -91
- data/ext/apex_ext/apex_src/docs/index.md +0 -459
- data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +0 -351
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +0 -745
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +0 -829
- data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +0 -1148
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +0 -161
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +0 -184
- data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +0 -211
- data/ext/apex_ext/apex_src/documentation/README.md +0 -160
- data/ext/apex_ext/apex_src/documentation/app-settings-table.md +0 -67
- data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +0 -340
- data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +0 -11
- data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +0 -234
- data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +0 -516
- data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +0 -461
- data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +0 -193
- data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +0 -201
- data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +0 -678
- data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +0 -1367
- data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +0 -371
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Info.plist +0 -24
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +0 -2211
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +0 -1194
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +0 -1620
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +0 -6024
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +0 -1877
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +0 -1040
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +0 -1402
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +0 -1656
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +0 -1226
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +0 -1148
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +0 -1478
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +0 -1225
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +0 -2327
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +0 -2041
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +0 -1314
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +0 -1868
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +0 -1731
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +0 -2242
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +0 -1533
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +0 -1362
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +0 -1338
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +0 -3188
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +0 -1405
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +0 -1902
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +0 -4222
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +0 -1781
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +0 -1629
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +0 -1394
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +0 -1132
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +0 -1754
- data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +0 -2562
- 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/docsets/ApexCLI.docset/Contents/Info.plist +0 -22
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Bold.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Bold_Italic.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Extrabold.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Extrabold_Italic.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Italic.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Semibold.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/cheatset_resources/Open_Sans_Semibold_Italic.woff +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/index.html +0 -914
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/Documents/style.css +0 -399
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/docSet.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/docsets/ApexCLI.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
- data/ext/apex_ext/apex_src/documentation/generate_app_docs.rb +0 -772
- data/ext/apex_ext/apex_src/documentation/generate_app_docs_ai.rb +0 -678
- data/ext/apex_ext/apex_src/documentation/generate_docset.rb +0 -873
- data/ext/apex_ext/apex_src/documentation/generate_single_html.rb +0 -733
- data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +0 -8746
- data/ext/apex_ext/apex_src/documentation/html/apex-docs.html +0 -17073
- data/ext/apex_ext/apex_src/documentation/shared_scripts.js +0 -64
- data/ext/apex_ext/apex_src/documentation/shared_styles.css +0 -651
- data/ext/apex_ext/apex_src/documentation/transform_for_app.example.md +0 -260
- data/ext/apex_ext/apex_src/documentation/transform_for_app.md +0 -55
- data/ext/apex_ext/apex_src/escaping-repro.md +0 -103
- data/ext/apex_ext/apex_src/examples/bracketed_spans_demo.md +0 -119
- data/ext/apex_ext/apex_src/examples/emoji_span_plugin.yml +0 -11
- data/ext/apex_ext/apex_src/examples/example.html +0 -53
- data/ext/apex_ext/apex_src/examples/example.md +0 -85
- data/ext/apex_ext/apex_src/examples/fenced_divs_demo.md +0 -158
- data/ext/apex_ext/apex_src/examples/kbd.md +0 -8
- data/ext/apex_ext/apex_src/examples/kbd_plugin.rb +0 -250
- data/ext/apex_ext/apex_src/examples/kbd_plugin.yml +0 -9
- data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +0 -8
- 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 +0 -547
- 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 +0 -33
- data/ext/apex_ext/apex_src/ial.html +0 -24
- data/ext/apex_ext/apex_src/icon/apexicon-outline-black.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon-outline-black@2x.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon-outline-mark.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon-outline-mark@2x.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon-outline-white.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon-outline-white@2x.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon.png +0 -0
- data/ext/apex_ext/apex_src/icon/apexicon@2x.png +0 -0
- data/ext/apex_ext/apex_src/man/apex-config.5 +0 -455
- data/ext/apex_ext/apex_src/man/apex-config.5.md +0 -262
- data/ext/apex_ext/apex_src/man/apex-plugins.7 +0 -541
- data/ext/apex_ext/apex_src/man/apex-plugins.7.md +0 -365
- data/ext/apex_ext/apex_src/man/apex.1 +0 -583
- data/ext/apex_ext/apex_src/man/apex.1.html +0 -703
- data/ext/apex_ext/apex_src/man/apex.1.md +0 -803
- data/ext/apex_ext/apex_src/man/apex.1.new +0 -338
- data/ext/apex_ext/apex_src/objc/Apex.swift +0 -316
- data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +0 -79
- data/ext/apex_ext/apex_src/objc/NSString+Apex.h +0 -207
- data/ext/apex_ext/apex_src/objc/NSString+Apex.m +0 -584
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +0 -76
- data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +0 -169
- data/ext/apex_ext/apex_src/output.html +0 -87
- data/ext/apex_ext/apex_src/pages/index.md +0 -459
- data/ext/apex_ext/apex_src/plans/quarto-mode.md +0 -442
- data/ext/apex_ext/apex_src/src/_README.md +0 -413
- data/ext/apex_ext/apex_src/src/extensions/github-emoji.txt +0 -869
- data/ext/apex_ext/apex_src/test-linux-build.sh +0 -20
- data/ext/apex_ext/apex_src/test.html +0 -103
- data/ext/apex_ext/apex_src/test_coverage.sh +0 -121
- data/ext/apex_ext/apex_src/test_ial_fenced.md +0 -6
- data/ext/apex_ext/apex_src/test_math_norm.py +0 -79
- data/ext/apex_ext/apex_src/test_pandoc_output.html +0 -48
- data/ext/apex_ext/apex_src/test_spm.sh +0 -107
- data/ext/apex_ext/apex_src/tests/ApexSPMTest/main.swift +0 -50
- data/ext/apex_ext/apex_src/tests/BENCHMARK_RESULTS.md +0 -229
- data/ext/apex_ext/apex_src/tests/CMakeLists.txt +0 -25
- data/ext/apex_ext/apex_src/tests/README.md +0 -152
- data/ext/apex_ext/apex_src/tests/benchmark.sh +0 -113
- data/ext/apex_ext/apex_src/tests/benchmark_comparison.sh +0 -166
- data/ext/apex_ext/apex_src/tests/compare_header_ids.sh +0 -31
- data/ext/apex_ext/apex_src/tests/fixtures/basic/headers.md +0 -25
- data/ext/apex_ext/apex_src/tests/fixtures/basic/list-interruption.md +0 -24
- data/ext/apex_ext/apex_src/tests/fixtures/basic/misc_markup.md +0 -33
- data/ext/apex_ext/apex_src/tests/fixtures/basic/test_basic.md +0 -26
- data/ext/apex_ext/apex_src/tests/fixtures/code/code-blocks.md +0 -260
- data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/SUMMARY.md +0 -6
- data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/chapter1.md +0 -7
- data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/index.txt +0 -9
- data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/intro.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/combine_summary/section1_1.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +0 -652
- data/ext/apex_ext/apex_src/tests/fixtures/debug_ref_image_ial.md +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/demos/ial.md +0 -11
- data/ext/apex_ext/apex_src/tests/fixtures/demos/ial_demo.md +0 -177
- data/ext/apex_ext/apex_src/tests/fixtures/extensions/emoji-autocorrect.md +0 -94
- data/ext/apex_ext/apex_src/tests/fixtures/extensions/emoji_test.md +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/extensions/kbd_test.md +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/filters/filter_output_with_rawblock.json +0 -1
- data/ext/apex_ext/apex_src/tests/fixtures/filters/unwrap.md +0 -7
- data/ext/apex_ext/apex_src/tests/fixtures/ial/bracketed_spans_test.md +0 -74
- data/ext/apex_ext/apex_src/tests/fixtures/images/auto-wildcard.md +0 -8
- data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +0 -10
- data/ext/apex_ext/apex_src/tests/fixtures/images/image_and_encoding_test.md +0 -27
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu.avif +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu.jpg +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu.webp +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu@2x.avif +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu@2x.jpg +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/img/app-pass-1-profile-menu@2x.webp +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/images/media_formats_test.md +0 -63
- data/ext/apex_ext/apex_src/tests/fixtures/images/multimarkdown_image_attributes_test.md +0 -60
- data/ext/apex_ext/apex_src/tests/fixtures/images/pandoc_ial_image_test.md +0 -27
- data/ext/apex_ext/apex_src/tests/fixtures/images/width_height_conversion_test.md +0 -94
- data/ext/apex_ext/apex_src/tests/fixtures/img-in-div.md +0 -16
- data/ext/apex_ext/apex_src/tests/fixtures/includes/code.py +0 -4
- data/ext/apex_ext/apex_src/tests/fixtures/includes/data-semi.csv +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/includes/data.csv +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/includes/data.tsv +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/includes/image.png +0 -2
- data/ext/apex_ext/apex_src/tests/fixtures/includes/metadata_options.yml +0 -11
- data/ext/apex_ext/apex_src/tests/fixtures/includes/nested.md +0 -8
- data/ext/apex_ext/apex_src/tests/fixtures/includes/raw.html +0 -4
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +0 -19
- data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +0 -11
- data/ext/apex_ext/apex_src/tests/fixtures/includes/simple.md +0 -7
- data/ext/apex_ext/apex_src/tests/fixtures/includes/test_image.png +0 -0
- data/ext/apex_ext/apex_src/tests/fixtures/includes/with space.txt +0 -1
- data/ext/apex_ext/apex_src/tests/fixtures/large_doc.md +0 -1094
- data/ext/apex_ext/apex_src/tests/fixtures/metadata/mmd-metadata.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/metadata/pandoc-meta.md +0 -4
- data/ext/apex_ext/apex_src/tests/fixtures/metadata/yaml-frontmatter.md +0 -6
- data/ext/apex_ext/apex_src/tests/fixtures/metadata_options.yml +0 -11
- data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +0 -2
- data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +0 -46
- data/ext/apex_ext/apex_src/tests/fixtures/output/gfm_header_id_test.md +0 -96
- data/ext/apex_ext/apex_src/tests/fixtures/output/test_citations.md +0 -43
- data/ext/apex_ext/apex_src/tests/fixtures/output/test_def_list_links.md +0 -12
- data/ext/apex_ext/apex_src/tests/fixtures/output/test_index_mmark.md +0 -53
- data/ext/apex_ext/apex_src/tests/fixtures/output/test_index_textindex.md +0 -37
- data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +0 -47
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +0 -3
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +0 -8
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +0 -19
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +0 -7
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +0 -81
- data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +0 -5
- data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +0 -47
- data/ext/apex_ext/apex_src/tests/fixtures/speed.md +0 -192
- data/ext/apex_ext/apex_src/tests/fixtures/tables/advanced_tables_test.md +0 -93
- data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +0 -37
- data/ext/apex_ext/apex_src/tests/fixtures/tables/inline_tables_test.md +0 -41
- data/ext/apex_ext/apex_src/tests/fixtures/tables/relaxed-table.md +0 -12
- data/ext/apex_ext/apex_src/tests/fixtures/tables/table_cr_line_endings.md +0 -15
- data/ext/apex_ext/apex_src/tests/fixtures/tables/table_no_trailing_newline.md +0 -15
- data/ext/apex_ext/apex_src/tests/generate_gfm_ids.sh +0 -105
- data/ext/apex_ext/apex_src/tests/generate_ial_demo.sh +0 -143
- data/ext/apex_ext/apex_src/tests/gfm_id_comparison_summary.md +0 -96
- data/ext/apex_ext/apex_src/tests/gh_api_test.md +0 -6
- data/ext/apex_ext/apex_src/tests/ial_demo.html +0 -186
- data/ext/apex_ext/apex_src/tests/include_code.py +0 -19
- data/ext/apex_ext/apex_src/tests/include_snippet.md +0 -15
- data/ext/apex_ext/apex_src/tests/metadata_cli_test.sh +0 -119
- data/ext/apex_ext/apex_src/tests/multi_file_cli_test.sh +0 -64
- data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +0 -80
- data/ext/apex_ext/apex_src/tests/sample_data.csv +0 -7
- data/ext/apex_ext/apex_src/tests/table_escaped_ltlt.md +0 -4
- data/ext/apex_ext/apex_src/tests/terminal_width_test.sh +0 -29
- data/ext/apex_ext/apex_src/tests/test-swift-package.sh +0 -14
- data/ext/apex_ext/apex_src/tests/test_basic.c +0 -74
- data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +0 -301
- data/ext/apex_ext/apex_src/tests/test_cmark_callback.c +0 -189
- data/ext/apex_ext/apex_src/tests/test_concordance.tsv +0 -4
- data/ext/apex_ext/apex_src/tests/test_custom_plugins.c +0 -78
- data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +0 -82
- data/ext/apex_ext/apex_src/tests/test_extensions.c +0 -3337
- data/ext/apex_ext/apex_src/tests/test_helpers.c +0 -183
- data/ext/apex_ext/apex_src/tests/test_helpers.h +0 -91
- data/ext/apex_ext/apex_src/tests/test_ial.c +0 -289
- data/ext/apex_ext/apex_src/tests/test_links.c +0 -418
- data/ext/apex_ext/apex_src/tests/test_marked_integration.c +0 -290
- data/ext/apex_ext/apex_src/tests/test_metadata.c +0 -1147
- data/ext/apex_ext/apex_src/tests/test_output.c +0 -2056
- data/ext/apex_ext/apex_src/tests/test_plugins.c +0 -219
- data/ext/apex_ext/apex_src/tests/test_refs.bib +0 -31
- data/ext/apex_ext/apex_src/tests/test_runner.c +0 -268
- data/ext/apex_ext/apex_src/tests/test_syntax_highlight.c +0 -198
- data/ext/apex_ext/apex_src/tests/test_tables.c +0 -1298
- data/ext/apex_ext/apex_src/tests/update_benchmarks.sh +0 -9
- data/ext/apex_ext/apex_src/tests/yaml_test.md +0 -13
- data/ext/apex_ext/apex_src/tests.rb +0 -39
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +0 -48
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/CheckFileOffsetBits.c +0 -14
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/CheckFileOffsetBits.cmake +0 -43
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/FindAsan.cmake +0 -74
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/Makefile.nmake +0 -38
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/README.md +0 -206
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/CMakeLists.txt +0 -30
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/cplusplus.cpp +0 -15
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/cplusplus.h +0 -16
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/harness.c +0 -111
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/harness.h +0 -35
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/api_test/main.c +0 -1169
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/appveyor.yml +0 -21
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-bq-flat.md +0 -16
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-bq-nested.md +0 -13
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-code.md +0 -11
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-fences.md +0 -14
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-heading.md +0 -9
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-hr.md +0 -10
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-html.md +0 -32
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-lheading.md +0 -8
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-list-flat.md +0 -67
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-list-nested.md +0 -36
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-ref-flat.md +0 -15
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/block-ref-nested.md +0 -17
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-autolink.md +0 -14
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-backticks.md +0 -3
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-flat.md +0 -5
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-nested.md +0 -5
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-em-worst.md +0 -5
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-entity.md +0 -11
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-escape.md +0 -15
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-html.md +0 -44
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-links-flat.md +0 -23
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-links-nested.md +0 -13
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/inline-newlines.md +0 -24
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/lorem1.md +0 -13
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/samples/rawtabs.md +0 -18
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/statistics.py +0 -595
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/bench/stats.py +0 -19
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/benchmarks.md +0 -33
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/changelog.txt +0 -1245
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/data/CaseFolding.txt +0 -1495
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/CMakeLists.txt +0 -119
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/ext_scanners.re +0 -92
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/CMakeLists.txt +0 -22
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/README.md +0 -12
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzz_quadratic.c +0 -91
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzz_quadratic_brackets.c +0 -110
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/fuzz/fuzzloop.sh +0 -28
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/CMakeLists.txt +0 -10
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/make_man_page.py +0 -133
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/man1/cmark-gfm.1 +0 -78
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/man/man3/cmark-gfm.3 +0 -1041
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/nmake.bat +0 -1
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/CMakeLists.txt +0 -230
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/cmark-gfm_version.h.in +0 -7
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +0 -38
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h.in +0 -76
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/libcmark-gfm.pc.in +0 -10
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/scanners.re +0 -365
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/suppressions +0 -10
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/CMakeLists.txt +0 -114
- 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/afl_test_cases/test.md +0 -49
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/cmark-fuzz.c +0 -58
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/cmark.py +0 -105
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/entity_tests.py +0 -67
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions-full-info-string.txt +0 -0
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions-table-prefer-style-attributes.txt +0 -38
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +0 -989
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/fuzzing_dictionary +0 -67
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/normalize.py +0 -194
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/pathological_tests.py +0 -160
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/regression.txt +0 -375
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/roundtrip_tests.py +0 -50
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/run-cmark-fuzz +0 -4
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/smart_punct.txt +0 -177
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/spec.txt +0 -10212
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/spec_tests.py +0 -152
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/toolchain-mingw32.cmake +0 -17
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/Dockerfile +0 -41
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/appveyor-build.bat +0 -13
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/make_entities_inc.py +0 -32
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/mkcasefold.pl +0 -22
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/xml2md.xsl +0 -319
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/tools/xml2md_gfm.xsl +0 -80
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/why-cmark-and-not-x.md +0 -104
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.js +0 -6
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.py +0 -37
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.rb +0 -15
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper.rkt +0 -208
- data/ext/apex_ext/apex_src/vendor/cmark-gfm/wrappers/wrapper_ext.py +0 -109
- data/ext/apex_ext/apex_src/vendor/libyaml/CMakeLists.txt +0 -160
- data/ext/apex_ext/apex_src/vendor/libyaml/Changes +0 -372
- data/ext/apex_ext/apex_src/vendor/libyaml/License +0 -20
- data/ext/apex_ext/apex_src/vendor/libyaml/Makefile.am +0 -51
- data/ext/apex_ext/apex_src/vendor/libyaml/ReadMe.md +0 -46
- data/ext/apex_ext/apex_src/vendor/libyaml/announcement.msg +0 -89
- data/ext/apex_ext/apex_src/vendor/libyaml/bootstrap +0 -3
- data/ext/apex_ext/apex_src/vendor/libyaml/cmake/config.h.in +0 -4
- data/ext/apex_ext/apex_src/vendor/libyaml/configure.ac +0 -73
- data/ext/apex_ext/apex_src/vendor/libyaml/doc/doxygen.cfg +0 -222
- data/ext/apex_ext/apex_src/vendor/libyaml/docker/README.mkd +0 -17
- data/ext/apex_ext/apex_src/vendor/libyaml/docker/alpine-3.7 +0 -26
- data/ext/apex_ext/apex_src/vendor/libyaml/docker/fedora-25 +0 -26
- data/ext/apex_ext/apex_src/vendor/libyaml/docker/ubuntu-14.04 +0 -29
- data/ext/apex_ext/apex_src/vendor/libyaml/docker/ubuntu-16.04 +0 -24
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/anchors.yaml +0 -10
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/array.yaml +0 -2
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/global-tag.yaml +0 -14
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/json.yaml +0 -1
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/mapping.yaml +0 -2
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/numbers.yaml +0 -1
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/strings.yaml +0 -7
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/tags.yaml +0 -7
- data/ext/apex_ext/apex_src/vendor/libyaml/examples/yaml-version.yaml +0 -3
- data/ext/apex_ext/apex_src/vendor/libyaml/include/Makefile.am +0 -17
- data/ext/apex_ext/apex_src/vendor/libyaml/include/yaml.h +0 -1999
- data/ext/apex_ext/apex_src/vendor/libyaml/pkg/ReadMe.md +0 -77
- data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/Dockerfile +0 -32
- data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/output/ReadMe +0 -1
- data/ext/apex_ext/apex_src/vendor/libyaml/pkg/docker/scripts/libyaml-dist.sh +0 -23
- data/ext/apex_ext/apex_src/vendor/libyaml/regression-inputs/clusterfuzz-testcase-minimized-5607885063061504.yml +0 -1
- data/ext/apex_ext/apex_src/vendor/libyaml/src/Makefile.am +0 -4
- data/ext/apex_ext/apex_src/vendor/libyaml/src/api.c +0 -1393
- data/ext/apex_ext/apex_src/vendor/libyaml/src/dumper.c +0 -394
- data/ext/apex_ext/apex_src/vendor/libyaml/src/emitter.c +0 -2358
- data/ext/apex_ext/apex_src/vendor/libyaml/src/loader.c +0 -544
- data/ext/apex_ext/apex_src/vendor/libyaml/src/parser.c +0 -1416
- data/ext/apex_ext/apex_src/vendor/libyaml/src/reader.c +0 -469
- data/ext/apex_ext/apex_src/vendor/libyaml/src/scanner.c +0 -3598
- data/ext/apex_ext/apex_src/vendor/libyaml/src/writer.c +0 -141
- data/ext/apex_ext/apex_src/vendor/libyaml/src/yaml_private.h +0 -684
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/CMakeLists.txt +0 -27
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/Makefile.am +0 -9
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/ReadMe.md +0 -63
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-deconstructor-alt.c +0 -800
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-deconstructor.c +0 -1127
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-reformatter-alt.c +0 -217
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/example-reformatter.c +0 -202
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-all-tests.sh +0 -29
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-dumper.c +0 -314
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-emitter-test-suite.c +0 -290
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-emitter.c +0 -327
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-loader.c +0 -63
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-parser-test-suite.c +0 -196
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-parser.c +0 -88
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/run-scanner.c +0 -63
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/test-reader.c +0 -354
- data/ext/apex_ext/apex_src/vendor/libyaml/tests/test-version.c +0 -29
- data/ext/apex_ext/apex_src/vendor/libyaml/yaml-0.1.pc.in +0 -10
- data/ext/apex_ext/apex_src/vendor/libyaml/yamlConfig.cmake.in +0 -16
- data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +0 -74
- data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +0 -22
- 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 +0 -44
- data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +0 -128
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +0 -5118
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +0 -8
- 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 +0 -8
- data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +0 -99
- data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +0 -452
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +0 -52
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +0 -55
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +0 -2455
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +0 -8
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +0 -171
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +0 -55
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +0 -220
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +0 -43
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +0 -7
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +0 -76
- data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +0 -10
|
@@ -1,3337 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extensions Tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
#include "test_helpers.h"
|
|
6
|
-
#include "apex/apex.h"
|
|
7
|
-
#include "../src/html_renderer.h"
|
|
8
|
-
#include "../src/extensions/advanced_footnotes.h"
|
|
9
|
-
#include <stdio.h>
|
|
10
|
-
#include <stdlib.h>
|
|
11
|
-
#include <string.h>
|
|
12
|
-
|
|
13
|
-
static char *replace_first_substring(const char *text,
|
|
14
|
-
const char *needle,
|
|
15
|
-
const char *replacement) {
|
|
16
|
-
const char *pos = strstr(text, needle);
|
|
17
|
-
if (!pos) {
|
|
18
|
-
return NULL;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
size_t prefix_len = (size_t)(pos - text);
|
|
22
|
-
size_t needle_len = strlen(needle);
|
|
23
|
-
size_t replacement_len = strlen(replacement);
|
|
24
|
-
size_t suffix_len = strlen(pos + needle_len);
|
|
25
|
-
char *result = malloc(prefix_len + replacement_len + suffix_len + 1);
|
|
26
|
-
if (!result) {
|
|
27
|
-
return NULL;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
memcpy(result, text, prefix_len);
|
|
31
|
-
memcpy(result + prefix_len, replacement, replacement_len);
|
|
32
|
-
memcpy(result + prefix_len + replacement_len, pos + needle_len, suffix_len + 1);
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
static char *include_preparse_plugin_callback(const char *text,
|
|
37
|
-
__attribute__((unused)) const char *id_plugin,
|
|
38
|
-
apex_plugin_phase_mask phase,
|
|
39
|
-
__attribute__((unused)) const apex_options *options) {
|
|
40
|
-
if (!(phase & APEX_PLUGIN_PHASE_PRE_PARSE) || !text) {
|
|
41
|
-
return NULL;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
char *current = strdup(text);
|
|
45
|
-
if (!current) {
|
|
46
|
-
return NULL;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
bool changed = false;
|
|
50
|
-
char *next = replace_first_substring(current, "Included Content", "Plugin Included Content");
|
|
51
|
-
if (next) {
|
|
52
|
-
free(current);
|
|
53
|
-
current = next;
|
|
54
|
-
changed = true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
next = replace_first_substring(current, "## Section 2", "## Plugin Section 2");
|
|
58
|
-
if (next) {
|
|
59
|
-
free(current);
|
|
60
|
-
current = next;
|
|
61
|
-
changed = true;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!changed) {
|
|
65
|
-
free(current);
|
|
66
|
-
return NULL;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return current;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
static void include_preparse_plugin_register(apex_plugin_manager *manager,
|
|
73
|
-
__attribute__((unused)) const apex_options *options) {
|
|
74
|
-
apex_plugin_register(manager,
|
|
75
|
-
"include-preparse-plugin",
|
|
76
|
-
APEX_PLUGIN_PHASE_PRE_PARSE,
|
|
77
|
-
include_preparse_plugin_callback);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
void test_math(void) {
|
|
81
|
-
int suite_failures = suite_start();
|
|
82
|
-
print_suite_title("Math Support Tests", false, true);
|
|
83
|
-
|
|
84
|
-
apex_options opts = apex_options_default();
|
|
85
|
-
opts.enable_math = true;
|
|
86
|
-
char *html;
|
|
87
|
-
|
|
88
|
-
/* Test inline math */
|
|
89
|
-
html = apex_markdown_to_html("Equation: $E=mc^2$", 18, &opts);
|
|
90
|
-
assert_contains(html, "class=\"math inline\"", "Inline math class");
|
|
91
|
-
assert_contains(html, "E=mc^2", "Math content preserved");
|
|
92
|
-
apex_free_string(html);
|
|
93
|
-
|
|
94
|
-
/* Test display math */
|
|
95
|
-
html = apex_markdown_to_html("$$x^2 + y^2 = z^2$$", 19, &opts);
|
|
96
|
-
assert_contains(html, "class=\"math display\"", "Display math class");
|
|
97
|
-
apex_free_string(html);
|
|
98
|
-
|
|
99
|
-
/* Test that regular dollars don't trigger */
|
|
100
|
-
html = apex_markdown_to_html("I have $5 and $10", 17, &opts);
|
|
101
|
-
if (strstr(html, "class=\"math") == NULL) {
|
|
102
|
-
test_result(true, "Dollar signs don't false trigger");
|
|
103
|
-
} else {
|
|
104
|
-
test_result(false, "Dollar signs false triggered");
|
|
105
|
-
}
|
|
106
|
-
apex_free_string(html);
|
|
107
|
-
|
|
108
|
-
/* Test that math/autolinks are not applied inside Liquid {% %} tags */
|
|
109
|
-
const char *liquid_md = "Before {% kbd $@3 %} after";
|
|
110
|
-
html = apex_markdown_to_html(liquid_md, strlen(liquid_md), &opts);
|
|
111
|
-
assert_contains(html, "{% kbd $@3 %}", "Liquid tag content preserved exactly");
|
|
112
|
-
assert_not_contains(html, "class=\"math", "No math span created inside Liquid tag");
|
|
113
|
-
assert_not_contains(html, "mailto:", "No email autolink created inside Liquid tag");
|
|
114
|
-
apex_free_string(html);
|
|
115
|
-
|
|
116
|
-
bool had_failures = suite_end(suite_failures);
|
|
117
|
-
print_suite_title("Math Support Tests", had_failures, false);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Test Critic Markup
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
|
-
void test_critic_markup(void) {
|
|
125
|
-
int suite_failures = suite_start();
|
|
126
|
-
print_suite_title("Critic Markup Tests", false, true);
|
|
127
|
-
|
|
128
|
-
apex_options opts = apex_options_default();
|
|
129
|
-
opts.enable_critic_markup = true;
|
|
130
|
-
opts.critic_mode = 2; /* CRITIC_MARKUP */
|
|
131
|
-
char *html;
|
|
132
|
-
|
|
133
|
-
/* Test addition - markup mode */
|
|
134
|
-
html = apex_markdown_to_html("Text {++added++} here", 21, &opts);
|
|
135
|
-
assert_contains(html, "<ins class=\"critic\">added</ins>", "Critic addition markup");
|
|
136
|
-
apex_free_string(html);
|
|
137
|
-
|
|
138
|
-
/* Test deletion - markup mode */
|
|
139
|
-
html = apex_markdown_to_html("Text {--deleted--} here", 23, &opts);
|
|
140
|
-
assert_contains(html, "<del class=\"critic\">deleted</del>", "Critic deletion markup");
|
|
141
|
-
apex_free_string(html);
|
|
142
|
-
|
|
143
|
-
/* Test highlight - markup mode */
|
|
144
|
-
html = apex_markdown_to_html("Text {==highlighted==} here", 27, &opts);
|
|
145
|
-
assert_contains(html, "<mark class=\"critic\">highlighted</mark>", "Critic highlight markup");
|
|
146
|
-
apex_free_string(html);
|
|
147
|
-
|
|
148
|
-
/* Test accept mode - apply all changes */
|
|
149
|
-
opts.critic_mode = 0; /* CRITIC_ACCEPT */
|
|
150
|
-
html = apex_markdown_to_html("Text {++added++} and {--deleted--} more {~~old~>new~~} done.", 61, &opts);
|
|
151
|
-
assert_contains(html, "added", "Accept mode includes additions");
|
|
152
|
-
assert_contains(html, "new", "Accept mode includes new text from substitution");
|
|
153
|
-
/* Should NOT contain markup tags or deleted text */
|
|
154
|
-
if (strstr(html, "<ins") == NULL && strstr(html, "<del") == NULL && strstr(html, "deleted") == NULL && strstr(html, "old") == NULL) {
|
|
155
|
-
test_result(true, "Accept mode removes markup and deletions");
|
|
156
|
-
} else {
|
|
157
|
-
test_result(false, "Accept mode has markup or deleted text");
|
|
158
|
-
}
|
|
159
|
-
apex_free_string(html);
|
|
160
|
-
|
|
161
|
-
/* Test reject mode - revert all changes */
|
|
162
|
-
opts.critic_mode = 1; /* CRITIC_REJECT */
|
|
163
|
-
html = apex_markdown_to_html("Text {++added++} and {--deleted--} more {~~old~>new~~} done.", 61, &opts);
|
|
164
|
-
assert_contains(html, "deleted", "Reject mode includes deletions");
|
|
165
|
-
assert_contains(html, "old", "Reject mode includes old text from substitution");
|
|
166
|
-
/* Should NOT contain markup tags or additions */
|
|
167
|
-
if (strstr(html, "<ins") == NULL && strstr(html, "<del") == NULL && strstr(html, "added") == NULL && strstr(html, "new") == NULL) {
|
|
168
|
-
tests_passed++;
|
|
169
|
-
tests_run++;
|
|
170
|
-
if (!errors_only_output) {
|
|
171
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Reject mode removes markup and additions\n");
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
tests_failed++;
|
|
175
|
-
tests_run++;
|
|
176
|
-
printf(COLOR_RED "✗" COLOR_RESET " Reject mode has markup or added text\n");
|
|
177
|
-
}
|
|
178
|
-
apex_free_string(html);
|
|
179
|
-
|
|
180
|
-
/* Test accept mode with comments and highlights */
|
|
181
|
-
opts.critic_mode = 0; /* CRITIC_ACCEPT */
|
|
182
|
-
html = apex_markdown_to_html("Text {==highlight==} and {>>comment<<} here.", 44, &opts);
|
|
183
|
-
assert_contains(html, "highlight", "Accept mode keeps highlights");
|
|
184
|
-
/* Comments should be removed */
|
|
185
|
-
if (strstr(html, "comment") == NULL) {
|
|
186
|
-
test_result(true, "Accept mode removes comments");
|
|
187
|
-
} else {
|
|
188
|
-
test_result(false, "Accept mode kept comment");
|
|
189
|
-
}
|
|
190
|
-
apex_free_string(html);
|
|
191
|
-
|
|
192
|
-
/* Test reject mode with comments and highlights */
|
|
193
|
-
opts.critic_mode = 1; /* CRITIC_REJECT */
|
|
194
|
-
html = apex_markdown_to_html("Text {==highlight==} and {>>comment<<} here.", 44, &opts);
|
|
195
|
-
/* Highlights should show text, comments should be removed, no markup tags */
|
|
196
|
-
assert_contains(html, "highlight", "Reject mode shows highlight text");
|
|
197
|
-
if (strstr(html, "comment") == NULL && strstr(html, "<mark") == NULL && strstr(html, "<span") == NULL) {
|
|
198
|
-
test_result(true, "Reject mode removes comments and markup tags");
|
|
199
|
-
} else {
|
|
200
|
-
test_result(false, "Reject mode has comments or markup tags");
|
|
201
|
-
}
|
|
202
|
-
apex_free_string(html);
|
|
203
|
-
|
|
204
|
-
bool had_failures = suite_end(suite_failures);
|
|
205
|
-
print_suite_title("Critic Markup Tests", had_failures, false);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Test processor modes
|
|
210
|
-
*/
|
|
211
|
-
|
|
212
|
-
void test_processor_modes(void) {
|
|
213
|
-
int suite_failures = suite_start();
|
|
214
|
-
print_suite_title("Processor Modes Tests", false, true);
|
|
215
|
-
|
|
216
|
-
const char *markdown = "# Test\n\n**bold**";
|
|
217
|
-
char *html;
|
|
218
|
-
|
|
219
|
-
/* Test CommonMark mode */
|
|
220
|
-
apex_options cm_opts = apex_options_for_mode(APEX_MODE_COMMONMARK);
|
|
221
|
-
html = apex_markdown_to_html(markdown, strlen(markdown), &cm_opts);
|
|
222
|
-
assert_contains(html, "<h1", "CommonMark mode works");
|
|
223
|
-
apex_free_string(html);
|
|
224
|
-
|
|
225
|
-
/* Test GFM mode */
|
|
226
|
-
apex_options gfm_opts = apex_options_for_mode(APEX_MODE_GFM);
|
|
227
|
-
html = apex_markdown_to_html(markdown, strlen(markdown), &gfm_opts);
|
|
228
|
-
assert_contains(html, "<strong>bold</strong>", "GFM mode works");
|
|
229
|
-
apex_free_string(html);
|
|
230
|
-
|
|
231
|
-
/* Test MultiMarkdown mode */
|
|
232
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
233
|
-
html = apex_markdown_to_html(markdown, strlen(markdown), &mmd_opts);
|
|
234
|
-
assert_contains(html, "<h1", "MultiMarkdown mode works");
|
|
235
|
-
apex_free_string(html);
|
|
236
|
-
|
|
237
|
-
/* Test Unified mode */
|
|
238
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
239
|
-
html = apex_markdown_to_html(markdown, strlen(markdown), &unified_opts);
|
|
240
|
-
assert_contains(html, "<h1", "Unified mode works");
|
|
241
|
-
apex_free_string(html);
|
|
242
|
-
|
|
243
|
-
/* Test Quarto mode */
|
|
244
|
-
apex_options quarto_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
245
|
-
html = apex_markdown_to_html(markdown, strlen(markdown), &quarto_opts);
|
|
246
|
-
assert_contains(html, "<h1", "Quarto mode works");
|
|
247
|
-
test_result(quarto_opts.enable_quarto_callouts == true, "Quarto mode enables quarto callouts by default");
|
|
248
|
-
test_result(quarto_opts.enable_quarto_extensions == true, "Quarto mode enables quarto extensions by default");
|
|
249
|
-
test_result(quarto_opts.enable_quarto_diagrams == true, "Quarto mode enables quarto diagrams by default");
|
|
250
|
-
test_result(quarto_opts.enable_quarto_shortcodes == true, "Quarto mode enables quarto shortcodes by default");
|
|
251
|
-
test_result(quarto_opts.enable_quarto_xrefs == true, "Quarto mode enables quarto xrefs by default");
|
|
252
|
-
test_result(quarto_opts.enable_quarto_strict_lists == false, "Quarto mode keeps strict lists off by default");
|
|
253
|
-
apex_free_string(html);
|
|
254
|
-
|
|
255
|
-
bool had_failures = suite_end(suite_failures);
|
|
256
|
-
print_suite_title("Processor Modes Tests", had_failures, false);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
void test_quarto_mode(void) {
|
|
260
|
-
int suite_failures = suite_start();
|
|
261
|
-
print_suite_title("Quarto Mode Tests", false, true);
|
|
262
|
-
|
|
263
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
264
|
-
char *html;
|
|
265
|
-
|
|
266
|
-
const char *callout =
|
|
267
|
-
"::: {.callout-warning}\n"
|
|
268
|
-
"Warning body.\n"
|
|
269
|
-
"::: ";
|
|
270
|
-
html = apex_markdown_to_html(callout, strlen(callout), &opts);
|
|
271
|
-
assert_contains(html, "class=\"callout", "Quarto mode renders callout blocks");
|
|
272
|
-
assert_contains(html, "Warning body", "Quarto callout body preserved");
|
|
273
|
-
apex_free_string(html);
|
|
274
|
-
|
|
275
|
-
const char *fig_alt = "{fig-alt=\"Alt text\"}";
|
|
276
|
-
html = apex_markdown_to_html(fig_alt, strlen(fig_alt), &opts);
|
|
277
|
-
assert_contains(html, "<figure>", "fig-alt image wrapped in figure");
|
|
278
|
-
assert_contains(html, "<figcaption>Caption</figcaption>", "fig-alt uses markdown alt as caption");
|
|
279
|
-
assert_contains(html, "alt=\"Alt text\"", "fig-alt maps to img alt attribute");
|
|
280
|
-
assert_not_contains(html, "fig-alt=", "fig-alt attribute stripped from output");
|
|
281
|
-
apex_free_string(html);
|
|
282
|
-
|
|
283
|
-
/* Direct convert test: fig-alt rewrites img alt for accessibility */
|
|
284
|
-
{
|
|
285
|
-
const char *in = "<p><img src=\"elephant.png\" alt=\"Caption\" fig-alt=\"Alt text\" /></p>";
|
|
286
|
-
char *out = apex_convert_image_captions(in, true, false);
|
|
287
|
-
assert_contains(out, "alt=\"Alt text\"", "fig-alt maps to img alt attribute (direct convert)");
|
|
288
|
-
assert_contains(out, "<figcaption>Caption</figcaption>", "fig-alt caption from markdown alt (direct convert)");
|
|
289
|
-
free(out);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const char *div_block = "::: {.sidebar}\nSidebar content.\n:::";
|
|
293
|
-
html = apex_markdown_to_html(div_block, strlen(div_block), &opts);
|
|
294
|
-
assert_contains(html, "class=\"sidebar\"", "Quarto mode renders fenced divs");
|
|
295
|
-
apex_free_string(html);
|
|
296
|
-
|
|
297
|
-
const char *raw_html_block =
|
|
298
|
-
"Before\n\n"
|
|
299
|
-
"```{=html}\n"
|
|
300
|
-
"<strong>raw</strong>\n"
|
|
301
|
-
"```\n\n"
|
|
302
|
-
"After";
|
|
303
|
-
html = apex_markdown_to_html(raw_html_block, strlen(raw_html_block), &opts);
|
|
304
|
-
assert_contains(html, "<strong>raw</strong>", "raw {=html} block passthrough");
|
|
305
|
-
assert_not_contains(html, "**raw**", "raw {=html} block not markdown-processed");
|
|
306
|
-
assert_not_contains(html, "<code", "raw {=html} block not wrapped in code");
|
|
307
|
-
apex_free_string(html);
|
|
308
|
-
|
|
309
|
-
const char *raw_latex_block =
|
|
310
|
-
"```{=latex}\n"
|
|
311
|
-
"\\textbf{Bold}\n"
|
|
312
|
-
"```";
|
|
313
|
-
html = apex_markdown_to_html(raw_latex_block, strlen(raw_latex_block), &opts);
|
|
314
|
-
assert_contains(html, "<!-- raw format=latex -->", "raw {=latex} block wrapped in comment");
|
|
315
|
-
assert_contains(html, "\\textbf{Bold}", "raw {=latex} body preserved in comment");
|
|
316
|
-
assert_not_contains(html, "<code", "raw {=latex} block not wrapped in code");
|
|
317
|
-
apex_free_string(html);
|
|
318
|
-
|
|
319
|
-
const char *raw_html_inline = "Text `<em>inline</em>`{=html} end.";
|
|
320
|
-
html = apex_markdown_to_html(raw_html_inline, strlen(raw_html_inline), &opts);
|
|
321
|
-
assert_contains(html, "<em>inline</em>", "inline {=html} passthrough");
|
|
322
|
-
assert_not_contains(html, "`{=html}`", "inline {=html} marker stripped");
|
|
323
|
-
apex_free_string(html);
|
|
324
|
-
|
|
325
|
-
/* Unified mode without quarto extensions leaves {=html} fences intact */
|
|
326
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
327
|
-
unified_opts.enable_quarto_extensions = false;
|
|
328
|
-
html = apex_markdown_to_html("```{=html}\n<b>x</b>\n```", 18, &unified_opts);
|
|
329
|
-
assert_contains(html, "<code", "non-quarto mode keeps {=html} as code fence");
|
|
330
|
-
apex_free_string(html);
|
|
331
|
-
|
|
332
|
-
const char *example_lists =
|
|
333
|
-
"(@) My first example will be numbered (1).\n"
|
|
334
|
-
"(@) My second example will be numbered (2).\n";
|
|
335
|
-
html = apex_markdown_to_html(example_lists, strlen(example_lists), &opts);
|
|
336
|
-
assert_contains(html, "<ol>", "example list (@) produces ordered list");
|
|
337
|
-
assert_contains(html, "My first example", "example list first item");
|
|
338
|
-
assert_contains(html, "My second example", "example list second item");
|
|
339
|
-
assert_not_contains(html, "(@)", "example list marker stripped");
|
|
340
|
-
apex_free_string(html);
|
|
341
|
-
|
|
342
|
-
const char *example_lists_break =
|
|
343
|
-
"(@) My first example will be numbered (1).\n"
|
|
344
|
-
"(@) My second example will be numbered (2).\n"
|
|
345
|
-
"\n"
|
|
346
|
-
"Explanation of examples.\n"
|
|
347
|
-
"\n"
|
|
348
|
-
"(@) My third example will be numbered (3).\n";
|
|
349
|
-
html = apex_markdown_to_html(example_lists_break, strlen(example_lists_break), &opts);
|
|
350
|
-
assert_contains(html, "Explanation of examples", "example list keeps interruption text");
|
|
351
|
-
assert_contains(html, "My third example", "example list third item after break");
|
|
352
|
-
assert_contains(html, "start=\"3\"", "example list continues numbering after interruption");
|
|
353
|
-
assert_not_contains(html, "(@)", "example list break strips marker");
|
|
354
|
-
apex_free_string(html);
|
|
355
|
-
|
|
356
|
-
const char *example_lists_labeled =
|
|
357
|
-
"(@good) This is a good example.\n";
|
|
358
|
-
html = apex_markdown_to_html(example_lists_labeled, strlen(example_lists_labeled), &opts);
|
|
359
|
-
assert_contains(html, "This is a good example", "labeled example list item content");
|
|
360
|
-
assert_not_contains(html, "(@good)", "labeled example list marker stripped");
|
|
361
|
-
apex_free_string(html);
|
|
362
|
-
|
|
363
|
-
const char *roman_list =
|
|
364
|
-
"i) First\n"
|
|
365
|
-
"ii) Second\n"
|
|
366
|
-
"iii) Third\n";
|
|
367
|
-
html = apex_markdown_to_html(roman_list, strlen(roman_list), &opts);
|
|
368
|
-
assert_contains(html, "list-style-type: lower-roman", "roman list lower-roman style");
|
|
369
|
-
assert_contains(html, "First", "roman list first item");
|
|
370
|
-
assert_contains(html, "Second", "roman list second item");
|
|
371
|
-
assert_not_contains(html, "<!-- apex-alpha-list-", "roman list marker comment stripped");
|
|
372
|
-
apex_free_string(html);
|
|
373
|
-
|
|
374
|
-
const char *line_block =
|
|
375
|
-
"| Line one\n"
|
|
376
|
-
"| preserved spaces\n"
|
|
377
|
-
"| Line three\n";
|
|
378
|
-
html = apex_markdown_to_html(line_block, strlen(line_block), &opts);
|
|
379
|
-
assert_contains(html, "class=\"line-block\"", "line block wrapper");
|
|
380
|
-
assert_contains(html, "class=\"line\">Line one", "line block first line");
|
|
381
|
-
assert_contains(html, "class=\"line\"> preserved spaces", "line block preserves inner spaces");
|
|
382
|
-
assert_contains(html, "class=\"line\">Line three", "line block third line");
|
|
383
|
-
apex_free_string(html);
|
|
384
|
-
|
|
385
|
-
const char *code_fence_attrs =
|
|
386
|
-
"```{.python filename=\"run.py\"}\n"
|
|
387
|
-
"print(\"hello\")\n"
|
|
388
|
-
"```\n";
|
|
389
|
-
html = apex_markdown_to_html(code_fence_attrs, strlen(code_fence_attrs), &opts);
|
|
390
|
-
assert_contains(html, "data-filename=\"run.py\"", "code fence filename attribute on pre");
|
|
391
|
-
assert_contains(html, "print", "code fence body preserved");
|
|
392
|
-
assert_not_contains(html, "{.python", "code fence braced info string normalized");
|
|
393
|
-
assert_not_contains(html, "apex-code-fence-attrs", "code fence marker comment stripped");
|
|
394
|
-
apex_free_string(html);
|
|
395
|
-
|
|
396
|
-
const char *code_fence_linenos =
|
|
397
|
-
"```{.python linenos=true}\n"
|
|
398
|
-
"print(\"hi\")\n"
|
|
399
|
-
"```\n";
|
|
400
|
-
html = apex_markdown_to_html(code_fence_linenos, strlen(code_fence_linenos), &opts);
|
|
401
|
-
assert_contains(html, "data-linenos=\"true\"", "code fence linenos attribute on pre");
|
|
402
|
-
apex_free_string(html);
|
|
403
|
-
|
|
404
|
-
const char *mermaid_fence =
|
|
405
|
-
"```{mermaid}\n"
|
|
406
|
-
"flowchart LR\n"
|
|
407
|
-
" A --> B\n"
|
|
408
|
-
"```\n";
|
|
409
|
-
html = apex_markdown_to_html(mermaid_fence, strlen(mermaid_fence), &opts);
|
|
410
|
-
assert_contains(html, "<pre class=\"mermaid\">", "mermaid fence renders pre.mermaid");
|
|
411
|
-
assert_contains(html, "flowchart LR", "mermaid fence body preserved");
|
|
412
|
-
assert_contains(html, "A --> B", "mermaid fence diagram content preserved");
|
|
413
|
-
assert_not_contains(html, "{mermaid}", "mermaid fence marker stripped");
|
|
414
|
-
assert_not_contains(html, "<code", "mermaid fence not wrapped in code");
|
|
415
|
-
apex_free_string(html);
|
|
416
|
-
|
|
417
|
-
const char *dot_fence =
|
|
418
|
-
"```{dot}\n"
|
|
419
|
-
"digraph { A -> B; }\n"
|
|
420
|
-
"```\n";
|
|
421
|
-
html = apex_markdown_to_html(dot_fence, strlen(dot_fence), &opts);
|
|
422
|
-
assert_contains(html, "<pre class=\"graphviz\">", "dot fence renders pre.graphviz");
|
|
423
|
-
assert_contains(html, "digraph", "dot fence body preserved");
|
|
424
|
-
apex_free_string(html);
|
|
425
|
-
|
|
426
|
-
const char *graphviz_fence =
|
|
427
|
-
"```{graphviz}\n"
|
|
428
|
-
"graph { A -- B; }\n"
|
|
429
|
-
"```\n";
|
|
430
|
-
html = apex_markdown_to_html(graphviz_fence, strlen(graphviz_fence), &opts);
|
|
431
|
-
assert_contains(html, "<pre class=\"graphviz\">", "graphviz fence renders pre.graphviz");
|
|
432
|
-
assert_contains(html, "graph { A -- B", "graphviz fence body preserved");
|
|
433
|
-
apex_free_string(html);
|
|
434
|
-
|
|
435
|
-
apex_options no_diagrams_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
436
|
-
no_diagrams_opts.enable_quarto_diagrams = false;
|
|
437
|
-
html = apex_markdown_to_html(mermaid_fence, strlen(mermaid_fence), &no_diagrams_opts);
|
|
438
|
-
assert_contains(html, "<code", "disabled quarto diagrams keeps mermaid as code fence");
|
|
439
|
-
assert_not_contains(html, "class=\"mermaid\"", "disabled quarto diagrams skips pre.mermaid");
|
|
440
|
-
apex_free_string(html);
|
|
441
|
-
|
|
442
|
-
apex_options no_unsafe_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
443
|
-
no_unsafe_opts.unsafe = false;
|
|
444
|
-
html = apex_markdown_to_html(mermaid_fence, strlen(mermaid_fence), &no_unsafe_opts);
|
|
445
|
-
assert_contains(html, "<code", "no-unsafe keeps mermaid fence as code block");
|
|
446
|
-
assert_not_contains(html, "class=\"mermaid\"", "no-unsafe skips raw pre.mermaid");
|
|
447
|
-
apex_free_string(html);
|
|
448
|
-
|
|
449
|
-
apex_options standalone_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
450
|
-
standalone_opts.standalone = true;
|
|
451
|
-
html = apex_markdown_to_html(mermaid_fence, strlen(mermaid_fence), &standalone_opts);
|
|
452
|
-
assert_contains(html, "mermaid.min.js", "standalone quarto auto-injects mermaid script");
|
|
453
|
-
assert_contains(html, "</body>", "standalone mermaid output is full document");
|
|
454
|
-
apex_free_string(html);
|
|
455
|
-
|
|
456
|
-
apex_options standalone_with_script = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
457
|
-
standalone_with_script.standalone = true;
|
|
458
|
-
standalone_with_script.script_tags = (char *[]){ strdup("<!-- mermaid already -->"), NULL };
|
|
459
|
-
html = apex_markdown_to_html(mermaid_fence, strlen(mermaid_fence), &standalone_with_script);
|
|
460
|
-
assert_not_contains(html, "mermaid.min.js", "standalone skips auto mermaid when script_tags mention mermaid");
|
|
461
|
-
assert_contains(html, "<!-- mermaid already -->", "standalone preserves user script_tags");
|
|
462
|
-
apex_free_string(html);
|
|
463
|
-
free(standalone_with_script.script_tags[0]);
|
|
464
|
-
|
|
465
|
-
const char *pagebreak_shortcode = "Page 1\n\n{{< pagebreak >}}\n\nPage 2";
|
|
466
|
-
html = apex_markdown_to_html(pagebreak_shortcode, strlen(pagebreak_shortcode), &opts);
|
|
467
|
-
assert_contains(html, "page-break-after", "pagebreak shortcode renders page break");
|
|
468
|
-
assert_contains(html, "Page 2", "content after pagebreak shortcode preserved");
|
|
469
|
-
assert_not_contains(html, "{{< pagebreak >}}", "pagebreak shortcode marker stripped");
|
|
470
|
-
apex_free_string(html);
|
|
471
|
-
|
|
472
|
-
const char *kbd_shortcode = "Press {{< kbd $@3 >}} to save.";
|
|
473
|
-
html = apex_markdown_to_html(kbd_shortcode, strlen(kbd_shortcode), &opts);
|
|
474
|
-
assert_contains(html, "{% kbd $@3 %}", "kbd shortcode converts to liquid tag");
|
|
475
|
-
assert_not_contains(html, "{{< kbd", "kbd shortcode marker stripped");
|
|
476
|
-
apex_free_string(html);
|
|
477
|
-
|
|
478
|
-
apex_options include_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
479
|
-
include_opts.enable_file_includes = true;
|
|
480
|
-
#ifdef TEST_FIXTURES_DIR
|
|
481
|
-
include_opts.base_directory = TEST_FIXTURES_DIR;
|
|
482
|
-
#else
|
|
483
|
-
include_opts.base_directory = "tests/fixtures/includes";
|
|
484
|
-
#endif
|
|
485
|
-
const char *include_shortcode = "Before\n\n{{< include simple.md >}}\n\nAfter";
|
|
486
|
-
html = apex_markdown_to_html(include_shortcode, strlen(include_shortcode), &include_opts);
|
|
487
|
-
assert_contains(html, "Included Content", "include shortcode expands file content");
|
|
488
|
-
assert_contains(html, "After", "content after include shortcode preserved");
|
|
489
|
-
assert_not_contains(html, "{{< include", "include shortcode marker stripped");
|
|
490
|
-
apex_free_string(html);
|
|
491
|
-
|
|
492
|
-
apex_options no_shortcodes_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
493
|
-
no_shortcodes_opts.enable_quarto_shortcodes = false;
|
|
494
|
-
html = apex_markdown_to_html(pagebreak_shortcode, strlen(pagebreak_shortcode), &no_shortcodes_opts);
|
|
495
|
-
assert_contains(html, "{{< pagebreak", "disabled quarto shortcodes leave marker in output");
|
|
496
|
-
assert_not_contains(html, "page-break-after", "disabled quarto shortcodes skip pagebreak expansion");
|
|
497
|
-
apex_free_string(html);
|
|
498
|
-
|
|
499
|
-
const char *unknown_shortcode = "Text {{< widget foo >}} end.";
|
|
500
|
-
html = apex_markdown_to_html(unknown_shortcode, strlen(unknown_shortcode), &opts);
|
|
501
|
-
assert_contains(html, "{{< widget foo", "unknown shortcode left unchanged");
|
|
502
|
-
apex_free_string(html);
|
|
503
|
-
|
|
504
|
-
const char *empty_div_reset =
|
|
505
|
-
"1. First\n\n::: {}\n\n1. Second\n";
|
|
506
|
-
html = apex_markdown_to_html(empty_div_reset, strlen(empty_div_reset), &opts);
|
|
507
|
-
assert_contains(html, "<ol>", "empty div list reset produces ordered lists");
|
|
508
|
-
{
|
|
509
|
-
const char *first_ol = strstr(html, "<ol>");
|
|
510
|
-
const char *second_ol = first_ol ? strstr(first_ol + 1, "<ol>") : NULL;
|
|
511
|
-
test_result(second_ol != NULL, "empty div separates two ordered lists");
|
|
512
|
-
}
|
|
513
|
-
apex_free_string(html);
|
|
514
|
-
|
|
515
|
-
const char *cross_ref = "See @fig-elephant and @sec-intro for details.";
|
|
516
|
-
html = apex_markdown_to_html(cross_ref, strlen(cross_ref), &opts);
|
|
517
|
-
assert_contains(html, "<span class=\"quarto-xref\">@fig-elephant</span>", "fig cross-ref wrapped");
|
|
518
|
-
assert_contains(html, "<span class=\"quarto-xref\">@sec-intro</span>", "sec cross-ref wrapped");
|
|
519
|
-
apex_free_string(html);
|
|
520
|
-
|
|
521
|
-
{
|
|
522
|
-
apex_options cite_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
523
|
-
cite_opts.base_directory = "tests";
|
|
524
|
-
const char *bib_files[] = { "test_refs.bib", NULL };
|
|
525
|
-
cite_opts.bibliography_files = (char **)bib_files;
|
|
526
|
-
const char *mixed_cite_xref =
|
|
527
|
-
"See [@doe99] and @fig-elephant.";
|
|
528
|
-
html = apex_markdown_to_html(mixed_cite_xref, strlen(mixed_cite_xref), &cite_opts);
|
|
529
|
-
assert_contains(html, "citation", "bracket citation processed with bibliography");
|
|
530
|
-
assert_contains(html, "doe99", "bracket citation key preserved with bibliography");
|
|
531
|
-
assert_contains(html, "<span class=\"quarto-xref\">@fig-elephant</span>",
|
|
532
|
-
"bare fig xref not parsed as citation when bibliography set");
|
|
533
|
-
assert_not_contains(html, "data-cites=\"fig-elephant\"", "fig xref not in citation data-cites");
|
|
534
|
-
apex_free_string(html);
|
|
535
|
-
|
|
536
|
-
const char *bracket_fig = "See [@fig-elephant].";
|
|
537
|
-
html = apex_markdown_to_html(bracket_fig, strlen(bracket_fig), &cite_opts);
|
|
538
|
-
assert_contains(html, "citation", "bracketed @fig- key still processed as citation");
|
|
539
|
-
assert_contains(html, "fig-elephant", "bracketed fig- citation key preserved");
|
|
540
|
-
apex_free_string(html);
|
|
541
|
-
|
|
542
|
-
apex_options no_xref_opts = cite_opts;
|
|
543
|
-
no_xref_opts.enable_quarto_xrefs = false;
|
|
544
|
-
const char *bare_fig = "See @fig-elephant.";
|
|
545
|
-
html = apex_markdown_to_html(bare_fig, strlen(bare_fig), &no_xref_opts);
|
|
546
|
-
assert_contains(html, "citation", "bare @fig- treated as citation when quarto-xrefs off");
|
|
547
|
-
assert_not_contains(html, "quarto-xref", "no xref wrapper when quarto-xrefs off");
|
|
548
|
-
apex_free_string(html);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
apex_options hidden_opts = apex_options_for_mode(APEX_MODE_QUARTO);
|
|
552
|
-
hidden_opts.standalone = true;
|
|
553
|
-
html = apex_markdown_to_html("::: {.hidden}\nHidden content.\n:::", 35, &hidden_opts);
|
|
554
|
-
assert_contains(html, "class=\"hidden\"", "hidden div class preserved");
|
|
555
|
-
assert_contains(html, ".hidden { display: none; }", "standalone quarto CSS hides .hidden");
|
|
556
|
-
apex_free_string(html);
|
|
557
|
-
|
|
558
|
-
apex_options unified_opts2 = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
559
|
-
unified_opts2.enable_quarto_extensions = false;
|
|
560
|
-
html = apex_markdown_to_html("```{.python}\nx\n```", 18, &unified_opts2);
|
|
561
|
-
assert_contains(html, "{.python}", "non-quarto mode keeps braced fence info string");
|
|
562
|
-
apex_free_string(html);
|
|
563
|
-
|
|
564
|
-
bool had_failures = suite_end(suite_failures);
|
|
565
|
-
print_suite_title("Quarto Mode Tests", had_failures, false);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Test cmark_init callback for custom extension registration
|
|
570
|
-
*/
|
|
571
|
-
static int cmark_init_callback_invoked = 0;
|
|
572
|
-
|
|
573
|
-
static void test_cmark_init_cb(struct cmark_parser *parser,
|
|
574
|
-
const struct apex_options *opts,
|
|
575
|
-
int cmark_opts,
|
|
576
|
-
__attribute__((unused)) void *user_data) {
|
|
577
|
-
(void)parser;
|
|
578
|
-
(void)opts;
|
|
579
|
-
(void)cmark_opts;
|
|
580
|
-
cmark_init_callback_invoked = 1;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
void test_cmark_init_callback(void) {
|
|
584
|
-
int suite_failures = suite_start();
|
|
585
|
-
print_suite_title("cmark_init callback Tests", false, true);
|
|
586
|
-
|
|
587
|
-
cmark_init_callback_invoked = 0;
|
|
588
|
-
apex_options opts = apex_options_default();
|
|
589
|
-
opts.cmark_init = test_cmark_init_cb;
|
|
590
|
-
char *html = apex_markdown_to_html("# Hi", 4, &opts);
|
|
591
|
-
test_result(cmark_init_callback_invoked == 1, "cmark_init callback was invoked");
|
|
592
|
-
assert_contains(html, "<h1", "Basic parsing still works with callback");
|
|
593
|
-
assert_contains(html, "Hi</h1>", "Header content preserved");
|
|
594
|
-
apex_free_string(html);
|
|
595
|
-
|
|
596
|
-
/* NULL callback: conversion works normally */
|
|
597
|
-
opts.cmark_init = NULL;
|
|
598
|
-
html = apex_markdown_to_html("**bold**", 8, &opts);
|
|
599
|
-
assert_contains(html, "<strong>bold</strong>", "Parsing works with NULL callback");
|
|
600
|
-
apex_free_string(html);
|
|
601
|
-
|
|
602
|
-
bool had_failures = suite_end(suite_failures);
|
|
603
|
-
print_suite_title("cmark_init callback Tests", had_failures, false);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Test MultiMarkdown-style image attributes (inline and reference)
|
|
608
|
-
*/
|
|
609
|
-
|
|
610
|
-
void test_multimarkdown_image_attributes(void) {
|
|
611
|
-
int suite_failures = suite_start();
|
|
612
|
-
print_suite_title("MultiMarkdown Image Attribute Tests", false, true);
|
|
613
|
-
|
|
614
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
615
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
616
|
-
|
|
617
|
-
const char *md =
|
|
618
|
-
"\n\n"
|
|
619
|
-
"\n\n"
|
|
620
|
-
"\n\n"
|
|
621
|
-
"\n\n"
|
|
622
|
-
"![Ref with attrs][ref-inline-1]\n\n"
|
|
623
|
-
"[ref-inline-1]: /images/test-ref-1.jpg width=200\n\n"
|
|
624
|
-
"![Ref with title][ref-inline-2]\n\n"
|
|
625
|
-
"[ref-inline-2]: /images/test-ref-2.jpg \"Falafel\" width=300\n\n"
|
|
626
|
-
"![Ref percent][ref-inline-3]\n\n"
|
|
627
|
-
"[ref-inline-3]: /images/test-ref-3.jpg width=50%\n\n"
|
|
628
|
-
"![Ref classes][ref-inline-4]\n\n"
|
|
629
|
-
"[ref-inline-4]: /images/test-ref-4.jpg \"Caption\" class=center shadow width=250 height=60%\n";
|
|
630
|
-
|
|
631
|
-
/* Helper lambda-style macro to run assertions in a given mode */
|
|
632
|
-
#define RUN_IMAGE_ATTR_TESTS(OPTS, MODE_LABEL) \
|
|
633
|
-
do { \
|
|
634
|
-
char *html = apex_markdown_to_html(md, strlen(md), &(OPTS)); \
|
|
635
|
-
assert_contains(html, "src=\"/images/test-inline-1.jpg\"", MODE_LABEL " inline url 1"); \
|
|
636
|
-
assert_contains(html, "width=\"200\"", MODE_LABEL " inline width=200"); \
|
|
637
|
-
assert_contains(html, "src=\"/images/test-inline-2.jpg\"", MODE_LABEL " inline url 2"); \
|
|
638
|
-
assert_contains(html, "title=\"Falafel\"", MODE_LABEL " inline title"); \
|
|
639
|
-
assert_contains(html, "width=\"300\"", MODE_LABEL " inline width=300"); \
|
|
640
|
-
assert_contains(html, "src=\"/images/test-inline-3.jpg\"", MODE_LABEL " inline url 3"); \
|
|
641
|
-
assert_contains(html, "style=\"width: 50%\"", MODE_LABEL " inline width 50% style"); \
|
|
642
|
-
assert_contains(html, "src=\"/images/test-inline-4.jpg\"", MODE_LABEL " inline url 4"); \
|
|
643
|
-
/* Class list ordering is not guaranteed; just check for center */ \
|
|
644
|
-
assert_contains(html, "class=\"center", MODE_LABEL " inline center class"); \
|
|
645
|
-
assert_contains(html, "width=\"250\"", MODE_LABEL " inline width=250"); \
|
|
646
|
-
assert_contains(html, "height: 60%", MODE_LABEL " inline height 60% style"); \
|
|
647
|
-
assert_contains(html, "src=\"/images/test-ref-1.jpg\"", MODE_LABEL " ref url 1"); \
|
|
648
|
-
assert_contains(html, "width=\"200\"", MODE_LABEL " ref width=200"); \
|
|
649
|
-
assert_contains(html, "src=\"/images/test-ref-2.jpg\"", MODE_LABEL " ref url 2"); \
|
|
650
|
-
assert_contains(html, "title=\"Falafel\"", MODE_LABEL " ref title"); \
|
|
651
|
-
assert_contains(html, "width=\"300\"", MODE_LABEL " ref width=300"); \
|
|
652
|
-
assert_contains(html, "src=\"/images/test-ref-3.jpg\"", MODE_LABEL " ref url 3"); \
|
|
653
|
-
assert_contains(html, "style=\"width: 50%\"", MODE_LABEL " ref width 50% style"); \
|
|
654
|
-
assert_contains(html, "src=\"/images/test-ref-4.jpg\"", MODE_LABEL " ref url 4"); \
|
|
655
|
-
assert_contains(html, "class=\"center", MODE_LABEL " ref center class"); \
|
|
656
|
-
assert_contains(html, "width=\"250\"", MODE_LABEL " ref width=250"); \
|
|
657
|
-
assert_contains(html, "height: 60%", MODE_LABEL " ref height 60% style"); \
|
|
658
|
-
apex_free_string(html); \
|
|
659
|
-
} while (0)
|
|
660
|
-
|
|
661
|
-
RUN_IMAGE_ATTR_TESTS(mmd_opts, "MMD");
|
|
662
|
-
RUN_IMAGE_ATTR_TESTS(unified_opts, "Unified");
|
|
663
|
-
|
|
664
|
-
#undef RUN_IMAGE_ATTR_TESTS
|
|
665
|
-
|
|
666
|
-
/* Test @2x srcset:  and  emit srcset="url 1x, url@2x 2x" */
|
|
667
|
-
{
|
|
668
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
669
|
-
const char *at2x_inline = "";
|
|
670
|
-
char *html = apex_markdown_to_html(at2x_inline, strlen(at2x_inline), &opts);
|
|
671
|
-
assert_contains(html, "src=\"img/icon_512x512.png\"", "@2x inline: src present");
|
|
672
|
-
assert_contains(html, "srcset=\"img/icon_512x512.png 1x, img/icon_512x512@2x.png 2x\"", "@2x inline: srcset 1x and 2x");
|
|
673
|
-
apex_free_string(html);
|
|
674
|
-
|
|
675
|
-
const char *at2x_title = "";
|
|
676
|
-
html = apex_markdown_to_html(at2x_title, strlen(at2x_title), &opts);
|
|
677
|
-
assert_contains(html, "srcset=\"img/icon_512x512.png 1x, img/icon_512x512@2x.png 2x\"", "@2x with title: srcset");
|
|
678
|
-
apex_free_string(html);
|
|
679
|
-
|
|
680
|
-
/* Reference-style: [ref]: url @2x */
|
|
681
|
-
const char *at2x_ref = "![Logo][logo]\n\n[logo]: img/hero.png @2x";
|
|
682
|
-
html = apex_markdown_to_html(at2x_ref, strlen(at2x_ref), &opts);
|
|
683
|
-
assert_contains(html, "srcset=\"img/hero.png 1x, img/hero@2x.png 2x\"", "@2x reference: srcset");
|
|
684
|
-
apex_free_string(html);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/* Reference-style image with attributes between two @2x images:
|
|
688
|
-
* - First image: inline with @2x, should get srcset
|
|
689
|
-
* - Middle image: reference-style with width/height/style, no @2x, should get attributes but no srcset
|
|
690
|
-
* - Last image: inline with @2x, should get srcset
|
|
691
|
-
* This guards against @2x or index bookkeeping causing the middle image to lose its attributes.
|
|
692
|
-
*/
|
|
693
|
-
{
|
|
694
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
695
|
-
const char *three_img_md =
|
|
696
|
-
"\n\n"
|
|
697
|
-
"![Badge][badge]\n\n"
|
|
698
|
-
"[badge]: img/badge.png width=250 height=83 style=\"margin: 0 auto;\"\n\n"
|
|
699
|
-
"\n";
|
|
700
|
-
|
|
701
|
-
char *html = apex_markdown_to_html(three_img_md, strlen(three_img_md), &opts);
|
|
702
|
-
|
|
703
|
-
/* First image: has srcset with @2x */
|
|
704
|
-
assert_contains(html,
|
|
705
|
-
"src=\"img/first.png\"",
|
|
706
|
-
"@2x three-image: first src present");
|
|
707
|
-
assert_contains(html,
|
|
708
|
-
"srcset=\"img/first.png 1x, img/first@2x.png 2x\"",
|
|
709
|
-
"@2x three-image: first srcset present");
|
|
710
|
-
|
|
711
|
-
/* Middle (badge) image: has width/height/style, but no srcset */
|
|
712
|
-
assert_contains(html,
|
|
713
|
-
"src=\"img/badge.png\"",
|
|
714
|
-
"@2x three-image: middle src present");
|
|
715
|
-
assert_contains(html,
|
|
716
|
-
"width=\"250\"",
|
|
717
|
-
"@2x three-image: middle width attribute");
|
|
718
|
-
assert_contains(html,
|
|
719
|
-
"height=\"83\"",
|
|
720
|
-
"@2x three-image: middle height attribute");
|
|
721
|
-
assert_contains(html,
|
|
722
|
-
"style=\"margin: 0 auto;\"",
|
|
723
|
-
"@2x three-image: middle style attribute");
|
|
724
|
-
assert_not_contains(html,
|
|
725
|
-
"img/badge@2x.png",
|
|
726
|
-
"@2x three-image: middle has no @2x srcset");
|
|
727
|
-
|
|
728
|
-
/* Last image: has srcset with @2x */
|
|
729
|
-
assert_contains(html,
|
|
730
|
-
"src=\"img/last.png\"",
|
|
731
|
-
"@2x three-image: last src present");
|
|
732
|
-
assert_contains(html,
|
|
733
|
-
"srcset=\"img/last.png 1x, img/last@2x.png 2x\"",
|
|
734
|
-
"@2x three-image: last srcset present");
|
|
735
|
-
|
|
736
|
-
apex_free_string(html);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/* @3x marker: behaves like @2x but emits both 2x and 3x entries in srcset */
|
|
740
|
-
{
|
|
741
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
742
|
-
const char *at3x_inline = "";
|
|
743
|
-
char *html = apex_markdown_to_html(at3x_inline, strlen(at3x_inline), &opts);
|
|
744
|
-
assert_contains(html, "src=\"img/icon_512x512.png\"", "@3x inline: src present");
|
|
745
|
-
assert_contains(html,
|
|
746
|
-
"srcset=\"img/icon_512x512.png 1x, img/icon_512x512@2x.png 2x, img/icon_512x512@3x.png 3x\"",
|
|
747
|
-
"@3x inline: srcset 1x, 2x, 3x");
|
|
748
|
-
apex_free_string(html);
|
|
749
|
-
|
|
750
|
-
/* Reference-style: [ref]: url @3x */
|
|
751
|
-
const char *at3x_ref = "![Logo][logo3]\n\n[logo3]: img/hero3.png @3x";
|
|
752
|
-
html = apex_markdown_to_html(at3x_ref, strlen(at3x_ref), &opts);
|
|
753
|
-
assert_contains(html,
|
|
754
|
-
"srcset=\"img/hero3.png 1x, img/hero3@2x.png 2x, img/hero3@3x.png 3x\"",
|
|
755
|
-
"@3x reference: srcset 1x, 2x, 3x");
|
|
756
|
-
apex_free_string(html);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/* webp attribute:  emits <picture> with webp source */
|
|
760
|
-
{
|
|
761
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
762
|
-
const char *webp_md = "";
|
|
763
|
-
char *html = apex_markdown_to_html(webp_md, strlen(webp_md), &opts);
|
|
764
|
-
assert_contains(html, "<picture>", "webp: picture element");
|
|
765
|
-
assert_contains(html, "type=\"image/webp\"", "webp: source type");
|
|
766
|
-
assert_contains(html, "img/hero.webp 1x", "webp: srcset");
|
|
767
|
-
assert_contains(html, "<img src=\"img/hero.png\"", "webp: img fallback");
|
|
768
|
-
assert_contains(html, "</picture>", "webp: picture close");
|
|
769
|
-
apex_free_string(html);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/* avif attribute:  emits <picture> with avif source */
|
|
773
|
-
{
|
|
774
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
775
|
-
const char *avif_md = "";
|
|
776
|
-
char *html = apex_markdown_to_html(avif_md, strlen(avif_md), &opts);
|
|
777
|
-
assert_contains(html, "<picture>", "avif: picture element");
|
|
778
|
-
assert_contains(html, "type=\"image/avif\"", "avif: source type");
|
|
779
|
-
assert_contains(html, "img/hero.avif 1x", "avif: srcset");
|
|
780
|
-
assert_contains(html, "<img src=\"img/hero.png\"", "avif: img fallback");
|
|
781
|
-
apex_free_string(html);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
/* webp + @2x: srcset includes 1x and 2x for webp */
|
|
785
|
-
{
|
|
786
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
787
|
-
const char *webp_2x_md = "";
|
|
788
|
-
char *html = apex_markdown_to_html(webp_2x_md, strlen(webp_2x_md), &opts);
|
|
789
|
-
assert_contains(html, "img/hero.webp 1x, img/hero@2x.webp 2x", "webp @2x: srcset");
|
|
790
|
-
apex_free_string(html);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
/* Picture conversion should preserve image IAL attrs on fallback <img> */
|
|
794
|
-
{
|
|
795
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
796
|
-
const char *picture_ial_md =
|
|
797
|
-
"[{:loading=lazy width=400 height=400}]"
|
|
798
|
-
"(img/text-basic@2x.jpg){.widget-shot.js-lightbox}";
|
|
799
|
-
char *html = apex_markdown_to_html(picture_ial_md, strlen(picture_ial_md), &opts);
|
|
800
|
-
assert_contains(html, "<a href=\"img/text-basic@2x.jpg\" class=\"widget-shot js-lightbox\">",
|
|
801
|
-
"picture+ial: outer link classes preserved");
|
|
802
|
-
assert_contains(html, "<picture>", "picture+ial: picture element");
|
|
803
|
-
assert_contains(html, "img/text-basic.avif 1x, img/text-basic@2x.avif 2x",
|
|
804
|
-
"picture+ial: avif srcset with @2x");
|
|
805
|
-
assert_contains(html,
|
|
806
|
-
"<img src=\"img/text-basic.jpg\" alt=\"text-basic widget screenshot\" width=\"400\" height=\"400\" loading=\"lazy\">",
|
|
807
|
-
"picture+ial: fallback img preserves IAL attrs");
|
|
808
|
-
apex_free_string(html);
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/* Raw HTML <a> must not steal/drop later markdown link IAL classes */
|
|
812
|
-
{
|
|
813
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
814
|
-
opts.enable_autolink = false;
|
|
815
|
-
opts.enable_image_captions = false;
|
|
816
|
-
const char *md =
|
|
817
|
-
"<div class=\"note\">\n"
|
|
818
|
-
"<p>See <a href=\"/integrations#getting-started\">Integrations</a>.</p>\n"
|
|
819
|
-
"</div>\n\n"
|
|
820
|
-
"- [{:loading=lazy width=400 height=400}]"
|
|
821
|
-
"(/x@2x.jpg){.widget-shot.js-lightbox}\n"
|
|
822
|
-
"{.g}\n";
|
|
823
|
-
char *html = apex_markdown_to_html(md, strlen(md), &opts);
|
|
824
|
-
assert_contains(html,
|
|
825
|
-
"<a href=\"/x@2x.jpg\" class=\"widget-shot js-lightbox\">",
|
|
826
|
-
"raw html <a> must not steal/drop later link IAL classes");
|
|
827
|
-
assert_contains(html,
|
|
828
|
-
"href=\"/integrations#getting-started\">Integrations</a>",
|
|
829
|
-
"raw html callout link remains unchanged");
|
|
830
|
-
assert_not_contains(html,
|
|
831
|
-
"href=\"/integrations#getting-started\" class=\"widget-shot",
|
|
832
|
-
"IAL must not be injected onto the raw html <a>");
|
|
833
|
-
apex_free_string(html);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
/* Distinct href IALs after a raw HTML <a> keep correct classes */
|
|
837
|
-
{
|
|
838
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
839
|
-
opts.enable_autolink = false;
|
|
840
|
-
const char *md =
|
|
841
|
-
"<p><a href=\"/raw\">Raw</a></p>\n\n"
|
|
842
|
-
"[One](/one){.class-one}\n\n"
|
|
843
|
-
"[Two](/two){.class-two}\n";
|
|
844
|
-
char *html = apex_markdown_to_html(md, strlen(md), &opts);
|
|
845
|
-
assert_contains(html, "<a href=\"/one\" class=\"class-one\">",
|
|
846
|
-
"distinct href after raw <a>: first link IAL");
|
|
847
|
-
assert_contains(html, "<a href=\"/two\" class=\"class-two\">",
|
|
848
|
-
"distinct href after raw <a>: second link IAL");
|
|
849
|
-
assert_not_contains(html, "href=\"/raw\" class=",
|
|
850
|
-
"distinct href after raw <a>: raw link unchanged");
|
|
851
|
-
apex_free_string(html);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/* Same-href markdown links after raw HTML <a>: disambiguate by order */
|
|
855
|
-
{
|
|
856
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
857
|
-
opts.enable_autolink = false;
|
|
858
|
-
const char *md =
|
|
859
|
-
"<p><a href=\"/same\">Raw</a></p>\n\n"
|
|
860
|
-
"[First](/same){.first}\n\n"
|
|
861
|
-
"[Second](/same){.second}\n";
|
|
862
|
-
char *html = apex_markdown_to_html(md, strlen(md), &opts);
|
|
863
|
-
assert_contains(html, "<a href=\"/same\" class=\"first\">First</a>",
|
|
864
|
-
"same href after raw <a>: first markdown link IAL");
|
|
865
|
-
assert_contains(html, "<a href=\"/same\" class=\"second\">Second</a>",
|
|
866
|
-
"same href after raw <a>: second markdown link IAL");
|
|
867
|
-
assert_not_contains(html, "href=\"/same\" class=\"first\">Raw",
|
|
868
|
-
"same href after raw <a>: raw link not given first IAL");
|
|
869
|
-
assert_not_contains(html, "href=\"/same\" class=\"second\">Raw",
|
|
870
|
-
"same href after raw <a>: raw link not given second IAL");
|
|
871
|
-
apex_free_string(html);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
/* Video URL:  emits <video> */
|
|
875
|
-
{
|
|
876
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
877
|
-
const char *video_md = "";
|
|
878
|
-
char *html = apex_markdown_to_html(video_md, strlen(video_md), &opts);
|
|
879
|
-
assert_contains(html, "<video", "video: video element");
|
|
880
|
-
assert_contains(html, "media/demo.mp4", "video: src");
|
|
881
|
-
assert_contains(html, "</video>", "video: close");
|
|
882
|
-
assert_not_contains(html, "<img", "video: no img");
|
|
883
|
-
apex_free_string(html);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
/* Video with webm attribute: adds webm source */
|
|
887
|
-
{
|
|
888
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
889
|
-
const char *video_webm_md = "";
|
|
890
|
-
char *html = apex_markdown_to_html(video_webm_md, strlen(video_webm_md), &opts);
|
|
891
|
-
assert_contains(html, "<source src=\"media/demo.webm\" type=\"video/webm\">", "video webm: source");
|
|
892
|
-
assert_contains(html, "<source src=\"media/demo.mp4\"", "video webm: primary fallback");
|
|
893
|
-
apex_free_string(html);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
/* Fixture-based tests: media_formats_test.md */
|
|
897
|
-
{
|
|
898
|
-
const char *fixture_path = "tests/fixtures/images/media_formats_test.md";
|
|
899
|
-
FILE *fp = fopen(fixture_path, "rb");
|
|
900
|
-
if (fp) {
|
|
901
|
-
fseek(fp, 0, SEEK_END);
|
|
902
|
-
long sz = ftell(fp);
|
|
903
|
-
fseek(fp, 0, SEEK_SET);
|
|
904
|
-
char *src = malloc(sz + 1);
|
|
905
|
-
if (src) {
|
|
906
|
-
size_t len = fread(src, 1, (size_t)sz, fp);
|
|
907
|
-
src[len] = '\0';
|
|
908
|
-
fclose(fp);
|
|
909
|
-
|
|
910
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
911
|
-
char *html = apex_markdown_to_html(src, len, &opts);
|
|
912
|
-
|
|
913
|
-
/* Images with webp/avif: picture elements */
|
|
914
|
-
assert_contains(html, "<picture>", "fixture: picture elements");
|
|
915
|
-
assert_contains(html, "type=\"image/webp\"", "fixture: webp source type");
|
|
916
|
-
assert_contains(html, "type=\"image/avif\"", "fixture: avif source type");
|
|
917
|
-
assert_contains(html, "img/hero.webp 1x", "fixture: webp srcset");
|
|
918
|
-
assert_contains(html, "img/hero.avif 1x", "fixture: avif srcset");
|
|
919
|
-
assert_contains(html, "img/hero@2x.webp 2x", "fixture: webp @2x");
|
|
920
|
-
assert_contains(html, "img/hero@2x.avif 2x", "fixture: avif @2x");
|
|
921
|
-
|
|
922
|
-
/* Videos: video elements */
|
|
923
|
-
assert_contains(html, "<video", "fixture: video elements");
|
|
924
|
-
assert_contains(html, "media/demo.mp4", "fixture: mp4 video");
|
|
925
|
-
assert_contains(html, "assets/trailer.mov", "fixture: mov video");
|
|
926
|
-
assert_contains(html, "assets/sample.m4v", "fixture: m4v video");
|
|
927
|
-
|
|
928
|
-
/* Video with format alternatives */
|
|
929
|
-
assert_contains(html, "media/demo.webm", "fixture: video webm source");
|
|
930
|
-
assert_contains(html, "media/intro.ogg", "fixture: video ogg source");
|
|
931
|
-
assert_contains(html, "media/clip.mp4", "fixture: webm with mp4 fallback");
|
|
932
|
-
|
|
933
|
-
/* Auto attribute (marker present; expansion requires base_directory) */
|
|
934
|
-
assert_contains(html, "data-apex-replace-auto=1", "fixture: auto marker");
|
|
935
|
-
|
|
936
|
-
apex_free_string(html);
|
|
937
|
-
free(src);
|
|
938
|
-
} else {
|
|
939
|
-
fclose(fp);
|
|
940
|
-
test_result(false, "fixture: malloc failed for media_formats_test.md");
|
|
941
|
-
}
|
|
942
|
-
} else {
|
|
943
|
-
test_result(false, "fixture: could not open media_formats_test.md");
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
/* auto attribute: emits data-apex-replace-auto (expansion requires base_directory) */
|
|
948
|
-
{
|
|
949
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
950
|
-
const char *auto_md = "";
|
|
951
|
-
char *html = apex_markdown_to_html(auto_md, strlen(auto_md), &opts);
|
|
952
|
-
assert_contains(html, "data-apex-replace-auto=1", "auto: emits replace marker");
|
|
953
|
-
apex_free_string(html);
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/* auto with base_directory: discovers img set (jpg, webp, avif, 2x variants), emits picture */
|
|
957
|
-
{
|
|
958
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
959
|
-
opts.base_directory = "tests/fixtures/images";
|
|
960
|
-
const char *auto_md = "";
|
|
961
|
-
char *html = apex_markdown_to_html(auto_md, strlen(auto_md), &opts);
|
|
962
|
-
assert_contains(html, "<picture>", "auto+base: picture element");
|
|
963
|
-
assert_contains(html, "type=\"image/avif\"", "auto+base: avif source");
|
|
964
|
-
assert_contains(html, "type=\"image/webp\"", "auto+base: webp source");
|
|
965
|
-
assert_contains(html, "app-pass-1-profile-menu.avif 1x", "auto+base: avif 1x");
|
|
966
|
-
assert_contains(html, "app-pass-1-profile-menu@2x.avif 2x", "auto+base: avif 2x");
|
|
967
|
-
assert_contains(html, "app-pass-1-profile-menu.webp 1x", "auto+base: webp 1x");
|
|
968
|
-
assert_contains(html, "app-pass-1-profile-menu@2x.webp 2x", "auto+base: webp 2x");
|
|
969
|
-
assert_contains(html, "<img src=\"img/app-pass-1-profile-menu.jpg\"", "auto+base: img fallback");
|
|
970
|
-
apex_free_string(html);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
/* * extension: equivalent to auto, discovers formats from base filename */
|
|
974
|
-
{
|
|
975
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
976
|
-
opts.base_directory = "tests/fixtures/images";
|
|
977
|
-
const char *wildcard_md = "";
|
|
978
|
-
char *html = apex_markdown_to_html(wildcard_md, strlen(wildcard_md), &opts);
|
|
979
|
-
assert_contains(html, "<picture>", "wildcard: picture element");
|
|
980
|
-
assert_contains(html, "type=\"image/avif\"", "wildcard: avif source");
|
|
981
|
-
assert_contains(html, "type=\"image/webp\"", "wildcard: webp source");
|
|
982
|
-
assert_contains(html, "app-pass-1-profile-menu.jpg", "wildcard: jpg fallback");
|
|
983
|
-
apex_free_string(html);
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/* * extension: emits data-apex-replace-auto marker when no base_directory */
|
|
987
|
-
{
|
|
988
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
989
|
-
const char *wildcard_md = "";
|
|
990
|
-
char *html = apex_markdown_to_html(wildcard_md, strlen(wildcard_md), &opts);
|
|
991
|
-
assert_contains(html, "data-apex-replace-auto=1", "wildcard: emits replace marker");
|
|
992
|
-
apex_free_string(html);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
bool had_failures = suite_end(suite_failures);
|
|
996
|
-
print_suite_title("MultiMarkdown Image Attribute Tests", had_failures, false);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
/**
|
|
1000
|
-
* Test file includes
|
|
1001
|
-
*/
|
|
1002
|
-
|
|
1003
|
-
void test_file_includes(void) {
|
|
1004
|
-
int suite_failures = suite_start();
|
|
1005
|
-
print_suite_title("File Includes Tests", false, true);
|
|
1006
|
-
|
|
1007
|
-
apex_options opts = apex_options_default();
|
|
1008
|
-
opts.enable_file_includes = true;
|
|
1009
|
-
#ifdef TEST_FIXTURES_DIR
|
|
1010
|
-
opts.base_directory = TEST_FIXTURES_DIR;
|
|
1011
|
-
#else
|
|
1012
|
-
opts.base_directory = "tests/fixtures/includes";
|
|
1013
|
-
#endif
|
|
1014
|
-
char *html;
|
|
1015
|
-
|
|
1016
|
-
/* Test Marked markdown include */
|
|
1017
|
-
html = apex_markdown_to_html("Before\n\n<<[simple.md]\n\nAfter", 28, &opts);
|
|
1018
|
-
assert_contains(html, "Included Content", "Marked markdown include");
|
|
1019
|
-
assert_contains(html, "List item 1", "Markdown processed from include");
|
|
1020
|
-
apex_free_string(html);
|
|
1021
|
-
|
|
1022
|
-
/* Test Marked code include */
|
|
1023
|
-
html = apex_markdown_to_html("Code:\n\n<<(code.py)\n\nDone", 23, &opts);
|
|
1024
|
-
assert_contains(html, "<pre", "Code include generates pre tag");
|
|
1025
|
-
assert_contains(html, "def hello", "Code content included");
|
|
1026
|
-
assert_contains(html, "lang=\"python\"", "Python language class added");
|
|
1027
|
-
apex_free_string(html);
|
|
1028
|
-
|
|
1029
|
-
/* Test Marked raw HTML include - currently uses placeholder */
|
|
1030
|
-
html = apex_markdown_to_html("HTML:\n\n<<{raw.html}\n\nDone", 24, &opts);
|
|
1031
|
-
assert_contains(html, "APEX_RAW_INCLUDE", "Raw HTML include marker present");
|
|
1032
|
-
apex_free_string(html);
|
|
1033
|
-
|
|
1034
|
-
/* Test MMD transclusion */
|
|
1035
|
-
html = apex_markdown_to_html("Include: {{simple.md}}", 22, &opts);
|
|
1036
|
-
assert_contains(html, "Included Content", "MMD transclusion works");
|
|
1037
|
-
apex_free_string(html);
|
|
1038
|
-
|
|
1039
|
-
apex_options plugin_opts = opts;
|
|
1040
|
-
plugin_opts.enable_plugins = true;
|
|
1041
|
-
plugin_opts.allow_external_plugin_detection = false;
|
|
1042
|
-
plugin_opts.plugin_register = include_preparse_plugin_register;
|
|
1043
|
-
html = apex_markdown_to_html("Include: {{simple.md}}", 22, &plugin_opts);
|
|
1044
|
-
assert_contains(html, "Plugin Included Content", "Included file runs through pre-parse plugin pipeline");
|
|
1045
|
-
apex_free_string(html);
|
|
1046
|
-
|
|
1047
|
-
const char *plugin_section_include = "![[sections#Section 2]]";
|
|
1048
|
-
html = apex_markdown_to_html(plugin_section_include, strlen(plugin_section_include), &plugin_opts);
|
|
1049
|
-
assert_contains(html, "Plugin Section 2", "Section include resolves before plugin rewrites section heading");
|
|
1050
|
-
assert_contains(html, "Nested section 2.1 text.", "Section include with plugin keeps nested subsection");
|
|
1051
|
-
assert_not_contains(html, "Section 3 text.", "Section include with plugin still stops at next same-level heading");
|
|
1052
|
-
apex_free_string(html);
|
|
1053
|
-
|
|
1054
|
-
/* Test MMD wildcard transclusion: file.* (legacy behavior) */
|
|
1055
|
-
html = apex_markdown_to_html("Include: {{simple.*}}", 22, &opts);
|
|
1056
|
-
assert_contains(html, "Included Content", "MMD wildcard file.* resolves to simple.md");
|
|
1057
|
-
apex_free_string(html);
|
|
1058
|
-
|
|
1059
|
-
/* Test CSV to table conversion */
|
|
1060
|
-
html = apex_markdown_to_html("Data:\n\n<<[data.csv]\n\nEnd", 24, &opts);
|
|
1061
|
-
assert_contains(html, "<table>", "CSV converts to table");
|
|
1062
|
-
assert_contains(html, "Alice", "CSV data in table");
|
|
1063
|
-
assert_contains(html, "New York", "CSV cell content");
|
|
1064
|
-
apex_free_string(html);
|
|
1065
|
-
|
|
1066
|
-
/* Test Marked CSV include with embedded shorthand delimiter override */
|
|
1067
|
-
const char *marked_csv_embedded_short = "Data:\n\n<<[data-semi.csv{;}]\n\nEnd";
|
|
1068
|
-
html = apex_markdown_to_html(marked_csv_embedded_short, strlen(marked_csv_embedded_short), &opts);
|
|
1069
|
-
assert_contains(html, "<table>", "Marked CSV include with embedded {;} converts to table");
|
|
1070
|
-
assert_contains(html, "San Francisco", "Marked CSV include with embedded {;} parses semicolon-separated values");
|
|
1071
|
-
assert_not_contains(html, "{;}", "Marked CSV include consumes embedded {;} delimiter token");
|
|
1072
|
-
apex_free_string(html);
|
|
1073
|
-
|
|
1074
|
-
/* Test Marked CSV include with embedded verbose delimiter override */
|
|
1075
|
-
const char *marked_csv_embedded_verbose = "Data:\n\n<<[data-semi.csv{delimiter=;}]\n\nEnd";
|
|
1076
|
-
html = apex_markdown_to_html(marked_csv_embedded_verbose, strlen(marked_csv_embedded_verbose), &opts);
|
|
1077
|
-
assert_contains(html, "<table>", "Marked CSV include with embedded {delimiter=;} converts to table");
|
|
1078
|
-
assert_contains(html, "Alice", "Marked CSV include with embedded {delimiter=;} parses semicolon-separated values");
|
|
1079
|
-
assert_not_contains(html, "{delimiter=;}", "Marked CSV include consumes embedded verbose delimiter token");
|
|
1080
|
-
apex_free_string(html);
|
|
1081
|
-
|
|
1082
|
-
/* Test TSV to table conversion */
|
|
1083
|
-
html = apex_markdown_to_html("{{data.tsv}}", 12, &opts);
|
|
1084
|
-
assert_contains(html, "<table>", "TSV converts to table");
|
|
1085
|
-
assert_contains(html, "Widget", "TSV data in table");
|
|
1086
|
-
apex_free_string(html);
|
|
1087
|
-
|
|
1088
|
-
/* Test MMD CSV transclusion with embedded shorthand delimiter override */
|
|
1089
|
-
const char *mmd_csv_embedded_short = "{{data-semi.csv{;}}}";
|
|
1090
|
-
html = apex_markdown_to_html(mmd_csv_embedded_short, strlen(mmd_csv_embedded_short), &opts);
|
|
1091
|
-
assert_contains(html, "<table>", "MMD CSV transclusion with embedded {;} converts to table");
|
|
1092
|
-
assert_contains(html, "San Francisco", "MMD CSV transclusion with embedded {;} parses semicolon-separated values");
|
|
1093
|
-
assert_not_contains(html, "{;}", "MMD CSV transclusion consumes embedded {;} delimiter token");
|
|
1094
|
-
apex_free_string(html);
|
|
1095
|
-
|
|
1096
|
-
/* Test MMD CSV transclusion with embedded verbose delimiter override */
|
|
1097
|
-
const char *mmd_csv_embedded_verbose = "{{data-semi.csv{delimiter=;}}}";
|
|
1098
|
-
html = apex_markdown_to_html(mmd_csv_embedded_verbose, strlen(mmd_csv_embedded_verbose), &opts);
|
|
1099
|
-
assert_contains(html, "<table>", "MMD CSV transclusion with embedded {delimiter=;} converts to table");
|
|
1100
|
-
assert_contains(html, "Alice", "MMD CSV transclusion with embedded {delimiter=;} parses semicolon-separated values");
|
|
1101
|
-
assert_not_contains(html, "{delimiter=;}", "MMD CSV transclusion consumes embedded verbose delimiter token");
|
|
1102
|
-
apex_free_string(html);
|
|
1103
|
-
|
|
1104
|
-
/* Test percent-encoded path in include */
|
|
1105
|
-
html = apex_markdown_to_html("<<[with%20space.txt]", 21, &opts);
|
|
1106
|
-
assert_contains(html, "Percent-decoded", "Percent-encoded path (with%20space.txt) resolves to file with space");
|
|
1107
|
-
apex_free_string(html);
|
|
1108
|
-
|
|
1109
|
-
/* Test iA Writer image include */
|
|
1110
|
-
html = apex_markdown_to_html("/image.png", 10, &opts);
|
|
1111
|
-
assert_contains(html, "<img", "iA Writer image include");
|
|
1112
|
-
assert_contains(html, "image.png", "Image path included");
|
|
1113
|
-
apex_free_string(html);
|
|
1114
|
-
|
|
1115
|
-
/* Test iA Writer code include */
|
|
1116
|
-
html = apex_markdown_to_html("/code.py", 8, &opts);
|
|
1117
|
-
assert_contains(html, "<pre", "iA Writer code include");
|
|
1118
|
-
assert_contains(html, "def hello", "Code included");
|
|
1119
|
-
apex_free_string(html);
|
|
1120
|
-
|
|
1121
|
-
/* Test iA Writer CSV include with verbose delimiter keyword */
|
|
1122
|
-
const char *ia_csv_delim_override_doc = "/data-semi.csv {delimiter=;}";
|
|
1123
|
-
html = apex_markdown_to_html(ia_csv_delim_override_doc, strlen(ia_csv_delim_override_doc), &opts);
|
|
1124
|
-
assert_contains(html, "<table>", "iA Writer CSV include with {delimiter=;} converts to table");
|
|
1125
|
-
assert_contains(html, "Alice", "iA Writer CSV include parses semicolon-separated values");
|
|
1126
|
-
assert_not_contains(html, "{delimiter=;}", "iA Writer include consumes verbose delimiter token");
|
|
1127
|
-
apex_free_string(html);
|
|
1128
|
-
|
|
1129
|
-
/* Test iA Writer CSV include with embedded shorthand delimiter (no whitespace) */
|
|
1130
|
-
const char *ia_csv_embedded_short = "/data-semi.csv{;}";
|
|
1131
|
-
html = apex_markdown_to_html(ia_csv_embedded_short, strlen(ia_csv_embedded_short), &opts);
|
|
1132
|
-
assert_contains(html, "<table>", "iA Writer CSV include with embedded {;} converts to table");
|
|
1133
|
-
assert_contains(html, "San Francisco", "iA Writer CSV include with embedded {;} parses semicolon-separated values");
|
|
1134
|
-
assert_not_contains(html, "{;}", "iA Writer include consumes embedded shorthand delimiter token");
|
|
1135
|
-
apex_free_string(html);
|
|
1136
|
-
|
|
1137
|
-
/* Test glob wildcard: *.md (should resolve to one of the .md fixtures) */
|
|
1138
|
-
html = apex_markdown_to_html("{{*.md}}", 10, &opts);
|
|
1139
|
-
if (strstr(html, "Included Content") != NULL ||
|
|
1140
|
-
strstr(html, "Nested Content") != NULL) {
|
|
1141
|
-
tests_passed++;
|
|
1142
|
-
tests_run++;
|
|
1143
|
-
if (!errors_only_output) {
|
|
1144
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Glob wildcard *.md resolves to a Markdown file\n");
|
|
1145
|
-
}
|
|
1146
|
-
} else {
|
|
1147
|
-
tests_failed++;
|
|
1148
|
-
tests_run++;
|
|
1149
|
-
test_result(false, "Glob wildcard *.md did not resolve correctly");
|
|
1150
|
-
}
|
|
1151
|
-
apex_free_string(html);
|
|
1152
|
-
|
|
1153
|
-
/* Test MMD address syntax - line range */
|
|
1154
|
-
html = apex_markdown_to_html("{{simple.md}}[3,5]", 20, &opts);
|
|
1155
|
-
assert_contains(html, "This is a simple", "Line range includes line 3");
|
|
1156
|
-
assert_contains(html, "markdown file", "Line range includes line 4");
|
|
1157
|
-
assert_not_contains(html, "Included Content", "Line range excludes line 1");
|
|
1158
|
-
assert_not_contains(html, "List item 1", "Line range excludes line 5 and beyond");
|
|
1159
|
-
apex_free_string(html);
|
|
1160
|
-
|
|
1161
|
-
/* Test MMD address syntax - from line to end */
|
|
1162
|
-
html = apex_markdown_to_html("{{simple.md}}[5,]", 19, &opts);
|
|
1163
|
-
assert_contains(html, "List item 1", "From line includes line 5");
|
|
1164
|
-
assert_contains(html, "List item 2", "From line includes later lines");
|
|
1165
|
-
assert_not_contains(html, "Included Content", "From line excludes earlier lines");
|
|
1166
|
-
apex_free_string(html);
|
|
1167
|
-
|
|
1168
|
-
/* Test MMD address syntax - prefix */
|
|
1169
|
-
html = apex_markdown_to_html("{{code.py}}[1,3;prefix=\"C: \"]", 30, &opts);
|
|
1170
|
-
assert_contains(html, "C: def hello()", "Prefix applied to included lines");
|
|
1171
|
-
assert_contains(html, "C: print", "Prefix applied to all lines");
|
|
1172
|
-
apex_free_string(html);
|
|
1173
|
-
|
|
1174
|
-
/* Test glob wildcard with single-character ?: c?de.py should resolve to code.py */
|
|
1175
|
-
html = apex_markdown_to_html("{{c?de.py}}", 12, &opts);
|
|
1176
|
-
assert_contains(html, "def hello", "? wildcard resolves to code.py");
|
|
1177
|
-
apex_free_string(html);
|
|
1178
|
-
|
|
1179
|
-
/* Test Marked address syntax - line range */
|
|
1180
|
-
html = apex_markdown_to_html("<<[simple.md][3,5]", 20, &opts);
|
|
1181
|
-
assert_contains(html, "This is a simple", "Marked syntax with line range");
|
|
1182
|
-
assert_not_contains(html, "Included Content", "Line range excludes header");
|
|
1183
|
-
apex_free_string(html);
|
|
1184
|
-
|
|
1185
|
-
/* Section extraction parity across include syntaxes */
|
|
1186
|
-
const char *mmd_section_include = "{{sections.md#Section 2}}";
|
|
1187
|
-
html = apex_markdown_to_html(mmd_section_include, strlen(mmd_section_include), &opts);
|
|
1188
|
-
assert_contains(html, "Section 2 text.", "MMD section include: selected section content");
|
|
1189
|
-
assert_contains(html, "Nested section 2.1 text.", "MMD section include: nested subsection included");
|
|
1190
|
-
assert_not_contains(html, "Section 3 text.", "MMD section include: stops before next same-level heading");
|
|
1191
|
-
apex_free_string(html);
|
|
1192
|
-
|
|
1193
|
-
const char *marked_section_include = "<<[sections.md#Section 2]";
|
|
1194
|
-
html = apex_markdown_to_html(marked_section_include, strlen(marked_section_include), &opts);
|
|
1195
|
-
assert_contains(html, "Section 2 text.", "Marked section include: selected section content");
|
|
1196
|
-
assert_contains(html, "Nested section 2.1 text.", "Marked section include: nested subsection included");
|
|
1197
|
-
assert_not_contains(html, "Section 3 text.", "Marked section include: stops before next same-level heading");
|
|
1198
|
-
apex_free_string(html);
|
|
1199
|
-
|
|
1200
|
-
const char *ia_section_include = "/sections.md#Section 2";
|
|
1201
|
-
html = apex_markdown_to_html(ia_section_include, strlen(ia_section_include), &opts);
|
|
1202
|
-
assert_contains(html, "Section 2 text.", "iA Writer section include: selected section content");
|
|
1203
|
-
assert_contains(html, "Nested section 2.1 text.", "iA Writer section include: nested subsection included");
|
|
1204
|
-
assert_not_contains(html, "Section 3 text.", "iA Writer section include: stops before next same-level heading");
|
|
1205
|
-
apex_free_string(html);
|
|
1206
|
-
|
|
1207
|
-
const char *obsidian_section_include = "![[sections#Section 2]]";
|
|
1208
|
-
html = apex_markdown_to_html(obsidian_section_include, strlen(obsidian_section_include), &opts);
|
|
1209
|
-
assert_contains(html, "Section 2 text.", "Obsidian embed include: selected section content");
|
|
1210
|
-
assert_contains(html, "Nested section 2.1 text.", "Obsidian embed include: nested subsection included");
|
|
1211
|
-
assert_not_contains(html, "Section 3 text.", "Obsidian embed include: stops before next same-level heading");
|
|
1212
|
-
apex_free_string(html);
|
|
1213
|
-
|
|
1214
|
-
opts.wikilink_extension = "txt";
|
|
1215
|
-
html = apex_markdown_to_html(obsidian_section_include, strlen(obsidian_section_include), &opts);
|
|
1216
|
-
assert_contains(html, "Section 2 text from TXT fixture.", "Obsidian embed include: uses --wikilink-extension default");
|
|
1217
|
-
assert_not_contains(html, "Nested section 2.1 text.", "Obsidian embed include: extension override changes source file");
|
|
1218
|
-
assert_not_contains(html, "Section 3 text from TXT fixture.", "Obsidian embed include: extension override still trims to section");
|
|
1219
|
-
apex_free_string(html);
|
|
1220
|
-
opts.wikilink_extension = ".txt";
|
|
1221
|
-
html = apex_markdown_to_html(obsidian_section_include, strlen(obsidian_section_include), &opts);
|
|
1222
|
-
assert_contains(html, "Section 2 text from TXT fixture.", "Obsidian embed include: dot-prefixed wikilink extension works");
|
|
1223
|
-
apex_free_string(html);
|
|
1224
|
-
opts.wikilink_extension = "html";
|
|
1225
|
-
html = apex_markdown_to_html(obsidian_section_include, strlen(obsidian_section_include), &opts);
|
|
1226
|
-
assert_contains(html, "Section 2 text.", "Obsidian embed include: falls back to .md when configured extension file is missing");
|
|
1227
|
-
apex_free_string(html);
|
|
1228
|
-
opts.wikilink_extension = NULL;
|
|
1229
|
-
|
|
1230
|
-
const char *missing_section_obsidian = "![[sections#Does Not Exist]]";
|
|
1231
|
-
html = apex_markdown_to_html(missing_section_obsidian, strlen(missing_section_obsidian), &opts);
|
|
1232
|
-
assert_contains(html, "Intro paragraph.", "Obsidian embed include: missing section falls back to full document");
|
|
1233
|
-
assert_contains(html, "Section 3 text.", "Obsidian embed include: full document fallback includes later sections");
|
|
1234
|
-
apex_free_string(html);
|
|
1235
|
-
|
|
1236
|
-
const char *missing_section_mmd = "{{sections.md#Does Not Exist}}";
|
|
1237
|
-
html = apex_markdown_to_html(missing_section_mmd, strlen(missing_section_mmd), &opts);
|
|
1238
|
-
assert_contains(html, "Intro paragraph.", "MMD include: missing section falls back to full document");
|
|
1239
|
-
assert_contains(html, "Section 3 text.", "MMD include: full document fallback includes later sections");
|
|
1240
|
-
apex_free_string(html);
|
|
1241
|
-
|
|
1242
|
-
/* Test Marked code include with address syntax */
|
|
1243
|
-
html = apex_markdown_to_html("<<(code.py)[1,3]", 18, &opts);
|
|
1244
|
-
assert_contains(html, "def hello()", "Code include with line range");
|
|
1245
|
-
assert_contains(html, "print", "Code include includes second line");
|
|
1246
|
-
assert_not_contains(html, "return True", "Code include excludes later lines");
|
|
1247
|
-
apex_free_string(html);
|
|
1248
|
-
|
|
1249
|
-
/* Test regex address syntax */
|
|
1250
|
-
html = apex_markdown_to_html("{{simple.md}}[/This is/,/List item/]", 36, &opts);
|
|
1251
|
-
assert_contains(html, "This is a simple", "Regex range includes matching line");
|
|
1252
|
-
assert_contains(html, "markdown file", "Regex range includes lines between matches");
|
|
1253
|
-
assert_not_contains(html, "Included Content", "Regex range excludes before first match");
|
|
1254
|
-
apex_free_string(html);
|
|
1255
|
-
|
|
1256
|
-
/* iA Writer plain include still includes full file when no section is requested */
|
|
1257
|
-
html = apex_markdown_to_html("/code.py", 8, &opts);
|
|
1258
|
-
assert_contains(html, "def hello()", "iA Writer syntax unchanged");
|
|
1259
|
-
assert_contains(html, "return True", "iA Writer includes full file");
|
|
1260
|
-
apex_free_string(html);
|
|
1261
|
-
|
|
1262
|
-
/* Test address syntax edge cases */
|
|
1263
|
-
/* Single line range - line 3 is the full sentence, so [3,4] includes only line 3 */
|
|
1264
|
-
html = apex_markdown_to_html("{{simple.md}}[3,4]", 20, &opts);
|
|
1265
|
-
assert_contains(html, "This is a simple", "Single line range works");
|
|
1266
|
-
assert_contains(html, "markdown file", "Single line includes full line 3");
|
|
1267
|
-
assert_not_contains(html, "List item 1", "Single line excludes line 5");
|
|
1268
|
-
apex_free_string(html);
|
|
1269
|
-
|
|
1270
|
-
/* Prefix with regex range - check if prefix is applied (may need to check implementation) */
|
|
1271
|
-
html = apex_markdown_to_html("{{simple.md}}[/This is/,/List item/;prefix=\" \"]", 48, &opts);
|
|
1272
|
-
assert_contains(html, "This is a simple", "Regex range includes matching line");
|
|
1273
|
-
/* Prefix application to regex ranges may need implementation verification */
|
|
1274
|
-
apex_free_string(html);
|
|
1275
|
-
|
|
1276
|
-
/* Prefix only (no line range) - verify prefix-only syntax is parsed */
|
|
1277
|
-
html = apex_markdown_to_html("{{code.py}}[prefix=\"// \"]", 26, &opts);
|
|
1278
|
-
assert_contains(html, "def hello()", "Prefix-only includes content");
|
|
1279
|
-
/* Prefix application may need implementation verification */
|
|
1280
|
-
apex_free_string(html);
|
|
1281
|
-
|
|
1282
|
-
/* Address syntax with CSV (should extract lines before conversion) */
|
|
1283
|
-
html = apex_markdown_to_html("{{data.csv}}[2,4]", 18, &opts);
|
|
1284
|
-
assert_contains(html, "<table>", "CSV with address converts to table");
|
|
1285
|
-
assert_contains(html, "Alice", "CSV address includes correct row");
|
|
1286
|
-
assert_not_contains(html, "Name,Age,City", "CSV address excludes header");
|
|
1287
|
-
apex_free_string(html);
|
|
1288
|
-
|
|
1289
|
-
/* Address syntax with Marked raw HTML */
|
|
1290
|
-
html = apex_markdown_to_html("<<{raw.html}[1,3]", 18, &opts);
|
|
1291
|
-
assert_contains(html, "APEX_RAW_INCLUDE", "Raw HTML include with address");
|
|
1292
|
-
apex_free_string(html);
|
|
1293
|
-
|
|
1294
|
-
/* Regex with no match (should return empty) */
|
|
1295
|
-
html = apex_markdown_to_html("{{simple.md}}[/NOTFOUND/,/ALSONOTFOUND/]", 44, &opts);
|
|
1296
|
-
/* Should not contain any content from file */
|
|
1297
|
-
if (strstr(html, "Included Content") == NULL && strstr(html, "List item") == NULL) {
|
|
1298
|
-
tests_passed++;
|
|
1299
|
-
tests_run++;
|
|
1300
|
-
if (!errors_only_output) {
|
|
1301
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Regex with no match returns empty\n");
|
|
1302
|
-
}
|
|
1303
|
-
} else {
|
|
1304
|
-
tests_failed++;
|
|
1305
|
-
tests_run++;
|
|
1306
|
-
test_result(false, "Regex with no match should return empty");
|
|
1307
|
-
}
|
|
1308
|
-
apex_free_string(html);
|
|
1309
|
-
|
|
1310
|
-
bool had_failures = suite_end(suite_failures);
|
|
1311
|
-
print_suite_title("File Includes Tests", had_failures, false);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
/**
|
|
1315
|
-
* Test IAL (Inline Attribute Lists)
|
|
1316
|
-
*/
|
|
1317
|
-
|
|
1318
|
-
void test_definition_lists(void) {
|
|
1319
|
-
int suite_failures = suite_start();
|
|
1320
|
-
print_suite_title("Definition Lists Tests", false, true);
|
|
1321
|
-
|
|
1322
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_KRAMDOWN);
|
|
1323
|
-
char *html;
|
|
1324
|
-
|
|
1325
|
-
/* Test basic definition list */
|
|
1326
|
-
html = apex_markdown_to_html("Term\n: Definition", 17, &opts);
|
|
1327
|
-
assert_contains(html, "<dl>", "Definition list tag");
|
|
1328
|
-
assert_contains(html, "<dt>Term</dt>", "Definition term");
|
|
1329
|
-
assert_contains(html, "<dd>Definition</dd>", "Definition description");
|
|
1330
|
-
apex_free_string(html);
|
|
1331
|
-
|
|
1332
|
-
/* Test multiple definitions */
|
|
1333
|
-
html = apex_markdown_to_html("Apple\n: A fruit\n: A company", 27, &opts);
|
|
1334
|
-
assert_contains(html, "<dt>Apple</dt>", "Multiple definitions term");
|
|
1335
|
-
assert_contains(html, "<dd>A fruit</dd>", "First definition");
|
|
1336
|
-
assert_contains(html, "<dd>A company</dd>", "Second definition");
|
|
1337
|
-
apex_free_string(html);
|
|
1338
|
-
|
|
1339
|
-
/* Test definition with Markdown content */
|
|
1340
|
-
const char *block_def = "Term\n: Definition with **bold** and *italic*";
|
|
1341
|
-
html = apex_markdown_to_html(block_def, strlen(block_def), &opts);
|
|
1342
|
-
assert_contains(html, "<dd>", "Definition created");
|
|
1343
|
-
assert_contains(html, "<strong>bold</strong>", "Bold markdown in definition");
|
|
1344
|
-
assert_contains(html, "<em>italic</em>", "Italic markdown in definition");
|
|
1345
|
-
apex_free_string(html);
|
|
1346
|
-
|
|
1347
|
-
/* Test multiple terms and definitions */
|
|
1348
|
-
const char *multi = "Term1\n: Def1\n\nTerm2\n: Def2";
|
|
1349
|
-
html = apex_markdown_to_html(multi, strlen(multi), &opts);
|
|
1350
|
-
assert_contains(html, "<dt>Term1</dt>", "First term");
|
|
1351
|
-
assert_contains(html, "<dt>Term2</dt>", "Second term");
|
|
1352
|
-
assert_contains(html, "<dd>Def1</dd>", "First definition");
|
|
1353
|
-
assert_contains(html, "<dd>Def2</dd>", "Second definition");
|
|
1354
|
-
apex_free_string(html);
|
|
1355
|
-
|
|
1356
|
-
/* Test inline links in definition list terms */
|
|
1357
|
-
const char *inline_link = "Term with [inline link](https://example.com)\n: Definition";
|
|
1358
|
-
html = apex_markdown_to_html(inline_link, strlen(inline_link), &opts);
|
|
1359
|
-
assert_contains(html, "<dt>", "Definition term with inline link");
|
|
1360
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Inline link in term has href");
|
|
1361
|
-
assert_contains(html, "inline link</a>", "Inline link text in term");
|
|
1362
|
-
apex_free_string(html);
|
|
1363
|
-
|
|
1364
|
-
/* Test reference-style links in definition list terms */
|
|
1365
|
-
const char *ref_link = "Term with [reference link][ref]\n: Definition\n\n[ref]: https://example.com \"Reference title\"";
|
|
1366
|
-
html = apex_markdown_to_html(ref_link, strlen(ref_link), &opts);
|
|
1367
|
-
assert_contains(html, "<dt>", "Definition term with reference link");
|
|
1368
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Reference link in term has href");
|
|
1369
|
-
assert_contains(html, "title=\"Reference title\"", "Reference link in term has title");
|
|
1370
|
-
assert_contains(html, "reference link</a>", "Reference link text in term");
|
|
1371
|
-
apex_free_string(html);
|
|
1372
|
-
|
|
1373
|
-
/* Test shortcut reference links in definition list terms */
|
|
1374
|
-
const char *shortcut_link = "Term with [shortcut][]\n: Definition\n\n[shortcut]: https://example.org";
|
|
1375
|
-
html = apex_markdown_to_html(shortcut_link, strlen(shortcut_link), &opts);
|
|
1376
|
-
assert_contains(html, "<dt>", "Definition term with shortcut reference");
|
|
1377
|
-
assert_contains(html, "<a href=\"https://example.org\"", "Shortcut reference in term has href");
|
|
1378
|
-
assert_contains(html, "shortcut</a>", "Shortcut reference text in term");
|
|
1379
|
-
apex_free_string(html);
|
|
1380
|
-
|
|
1381
|
-
/* Test inline links in definition descriptions */
|
|
1382
|
-
const char *def_inline = "Term\n: Definition with [inline link](https://example.com)";
|
|
1383
|
-
html = apex_markdown_to_html(def_inline, strlen(def_inline), &opts);
|
|
1384
|
-
assert_contains(html, "<dd>", "Definition with inline link");
|
|
1385
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Inline link in definition has href");
|
|
1386
|
-
apex_free_string(html);
|
|
1387
|
-
|
|
1388
|
-
/* Test reference-style links in definition descriptions */
|
|
1389
|
-
const char *def_ref = "Term\n: Definition with [reference][ref]\n\n[ref]: https://example.com";
|
|
1390
|
-
html = apex_markdown_to_html(def_ref, strlen(def_ref), &opts);
|
|
1391
|
-
assert_contains(html, "<dd>", "Definition with reference link");
|
|
1392
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Reference link in definition has href");
|
|
1393
|
-
apex_free_string(html);
|
|
1394
|
-
|
|
1395
|
-
/* Test definition list with blank line between term and first definition */
|
|
1396
|
-
const char *blank_before = "Term\n\n: definition 1\n: definition 2";
|
|
1397
|
-
html = apex_markdown_to_html(blank_before, strlen(blank_before), &opts);
|
|
1398
|
-
assert_contains(html, "<dl>", "Definition list with blank before first definition");
|
|
1399
|
-
assert_contains(html, "<dt>Term</dt>", "Term preserved across blank line");
|
|
1400
|
-
assert_contains(html, "<dd>definition 1</dd>", "First definition after blank line");
|
|
1401
|
-
assert_contains(html, "<dd>definition 2</dd>", "Second definition");
|
|
1402
|
-
apex_free_string(html);
|
|
1403
|
-
|
|
1404
|
-
/* Test definition list with blank line between definitions */
|
|
1405
|
-
const char *blank_between = "Term\n: definition 1\n\n: definition 2";
|
|
1406
|
-
html = apex_markdown_to_html(blank_between, strlen(blank_between), &opts);
|
|
1407
|
-
assert_contains(html, "<dl>", "Definition list with blank between definitions");
|
|
1408
|
-
assert_contains(html, "<dt>Term</dt>", "Term in list with blank between definitions");
|
|
1409
|
-
assert_contains(html, "<dd>definition 1</dd>", "First definition");
|
|
1410
|
-
assert_contains(html, "<dd>definition 2</dd>", "Second definition after blank line");
|
|
1411
|
-
apex_free_string(html);
|
|
1412
|
-
|
|
1413
|
-
/* Test definition list with blank lines everywhere (user's exact case) */
|
|
1414
|
-
const char *blank_everywhere = "Term\n\n: definition 1\n\n: definition 2";
|
|
1415
|
-
html = apex_markdown_to_html(blank_everywhere, strlen(blank_everywhere), &opts);
|
|
1416
|
-
assert_contains(html, "<dl>", "Definition list with blank lines everywhere");
|
|
1417
|
-
assert_contains(html, "<dt>Term</dt>", "Term preserved with multiple blank lines");
|
|
1418
|
-
assert_contains(html, "<dd>definition 1</dd>", "First definition");
|
|
1419
|
-
assert_contains(html, "<dd>definition 2</dd>", "Second definition");
|
|
1420
|
-
apex_free_string(html);
|
|
1421
|
-
|
|
1422
|
-
/* Test all four definition list formats */
|
|
1423
|
-
html = apex_markdown_to_html("term\n: definition", 17, &opts);
|
|
1424
|
-
assert_contains(html, "<dt>term</dt>", "Kramdown format: term + : definition");
|
|
1425
|
-
assert_contains(html, "<dd>definition</dd>", "Kramdown format dd");
|
|
1426
|
-
apex_free_string(html);
|
|
1427
|
-
|
|
1428
|
-
html = apex_markdown_to_html("term\n:: definition", 18, &opts);
|
|
1429
|
-
assert_contains(html, "<dt>term</dt>", "Kramdown format: term + :: definition");
|
|
1430
|
-
assert_contains(html, "<dd>definition</dd>", "Kramdown :: format dd");
|
|
1431
|
-
apex_free_string(html);
|
|
1432
|
-
|
|
1433
|
-
html = apex_markdown_to_html("term::definition", 16, &opts);
|
|
1434
|
-
assert_contains(html, "<dt>term</dt>", "One-line format: term::definition");
|
|
1435
|
-
assert_contains(html, "<dd>definition</dd>", "One-line format dd");
|
|
1436
|
-
apex_free_string(html);
|
|
1437
|
-
|
|
1438
|
-
html = apex_markdown_to_html("term :: definition", 18, &opts);
|
|
1439
|
-
assert_contains(html, "<dt>term</dt>", "One-line format: term :: definition");
|
|
1440
|
-
assert_contains(html, "<dd>definition</dd>", "One-line format with spaces dd");
|
|
1441
|
-
apex_free_string(html);
|
|
1442
|
-
|
|
1443
|
-
/* One-line definition NOT converted inside inline code span */
|
|
1444
|
-
html = apex_markdown_to_html("Use `term::definition` for syntax", 32, &opts);
|
|
1445
|
-
assert_contains(html, "<code>term::definition</code>", "One-line def preserved in inline code");
|
|
1446
|
-
assert_not_contains(html, "<dt>term</dt>", "No definition list from content inside backticks");
|
|
1447
|
-
apex_free_string(html);
|
|
1448
|
-
|
|
1449
|
-
/* One-line definition NOT converted inside fenced code block */
|
|
1450
|
-
const char *fenced_def = "```\nfoo::bar\nterm::definition\n```";
|
|
1451
|
-
html = apex_markdown_to_html(fenced_def, strlen(fenced_def), &opts);
|
|
1452
|
-
assert_contains(html, "foo::bar", "One-line def in fenced block preserved");
|
|
1453
|
-
assert_contains(html, "term::definition", "Second one-line def in fenced block preserved");
|
|
1454
|
-
assert_not_contains(html, "<dt>foo</dt>", "No definition list from fenced code content");
|
|
1455
|
-
apex_free_string(html);
|
|
1456
|
-
|
|
1457
|
-
/* One-line definition NOT converted inside indented code block */
|
|
1458
|
-
const char *indented_def = " key::value\n term::definition";
|
|
1459
|
-
html = apex_markdown_to_html(indented_def, strlen(indented_def), &opts);
|
|
1460
|
-
assert_contains(html, "key::value", "One-line def in indented block preserved");
|
|
1461
|
-
assert_contains(html, "term::definition", "Second one-line def in indented block preserved");
|
|
1462
|
-
assert_not_contains(html, "<dt>key</dt>", "No definition list from indented code content");
|
|
1463
|
-
apex_free_string(html);
|
|
1464
|
-
|
|
1465
|
-
/* Kramdown : definition NOT converted inside multi-line inline code span */
|
|
1466
|
-
const char *multiline_code = "`term::works\n :more:`";
|
|
1467
|
-
html = apex_markdown_to_html(multiline_code, strlen(multiline_code), &opts);
|
|
1468
|
-
assert_contains(html, "term::works", "One-line def in multi-line inline code preserved");
|
|
1469
|
-
assert_not_contains(html, "<dt>term</dt>", "No definition list from multi-line inline code");
|
|
1470
|
-
apex_free_string(html);
|
|
1471
|
-
|
|
1472
|
-
/* Leanpub {::pagebreak /} must not trigger definition list reordering */
|
|
1473
|
-
const char *heading_pagebreak = "## Page breaks\n {::pagebreak /}";
|
|
1474
|
-
html = apex_markdown_to_html(heading_pagebreak, strlen(heading_pagebreak), &opts);
|
|
1475
|
-
assert_contains(html, "<h2", "Heading before indented Leanpub pagebreak marker");
|
|
1476
|
-
assert_contains(html, "Page", "Heading text preserved");
|
|
1477
|
-
assert_contains(html, "<pre><code>{::pagebreak /}", "Leanpub pagebreak marker in code block");
|
|
1478
|
-
assert_not_contains(html, "<dl>", "Leanpub pagebreak marker did not create definition list");
|
|
1479
|
-
const char *h2_pos = strstr(html, "<h2");
|
|
1480
|
-
const char *pre_pos = strstr(html, "<pre>");
|
|
1481
|
-
if (h2_pos && pre_pos && h2_pos < pre_pos) {
|
|
1482
|
-
test_result(true, "Heading appears before code block with Leanpub pagebreak marker");
|
|
1483
|
-
} else {
|
|
1484
|
-
test_result(false, "Heading should appear before code block with Leanpub pagebreak marker");
|
|
1485
|
-
}
|
|
1486
|
-
apex_free_string(html);
|
|
1487
|
-
|
|
1488
|
-
/* Buffered term before fenced code block must not be discarded */
|
|
1489
|
-
const char *term_before_code =
|
|
1490
|
-
"You can now use these environment variables in your Run Command actions:\n\n"
|
|
1491
|
-
"```bash\n"
|
|
1492
|
-
"echo hello\n"
|
|
1493
|
-
"```";
|
|
1494
|
-
html = apex_markdown_to_html(term_before_code, strlen(term_before_code), &opts);
|
|
1495
|
-
assert_contains(html, "Run Command actions", "Intro paragraph preserved before code block");
|
|
1496
|
-
{
|
|
1497
|
-
const char *intro_pos = strstr(html, "Run Command actions");
|
|
1498
|
-
const char *code_pos = strstr(html, "<pre");
|
|
1499
|
-
test_result(intro_pos && code_pos && intro_pos < code_pos,
|
|
1500
|
-
"Intro paragraph appears before fenced code block");
|
|
1501
|
-
}
|
|
1502
|
-
apex_free_string(html);
|
|
1503
|
-
|
|
1504
|
-
/* Fence immediately after definition list must become a real code block */
|
|
1505
|
-
{
|
|
1506
|
-
const char *dl_then_fence =
|
|
1507
|
-
"Term\n"
|
|
1508
|
-
": Def\n"
|
|
1509
|
-
"\n"
|
|
1510
|
-
"```\n"
|
|
1511
|
-
"code\n"
|
|
1512
|
-
"```\n";
|
|
1513
|
-
html = apex_markdown_to_html(dl_then_fence, strlen(dl_then_fence), &opts);
|
|
1514
|
-
assert_contains(html, "<dl>", "DL before bare fence still renders");
|
|
1515
|
-
assert_contains(html, "<pre", "Bare fence after DL becomes pre/code");
|
|
1516
|
-
assert_contains(html, "<code", "Bare fence after DL contains code element");
|
|
1517
|
-
assert_not_contains(html, "</dl>\n```", "Fence after DL is not left as raw backticks");
|
|
1518
|
-
apex_free_string(html);
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
/* Paragraph between DL and fence stays before the fence */
|
|
1522
|
-
{
|
|
1523
|
-
const char *dl_para_fence =
|
|
1524
|
-
"Term\n"
|
|
1525
|
-
": Def\n"
|
|
1526
|
-
"\n"
|
|
1527
|
-
"This should stay before the diagram\n"
|
|
1528
|
-
"\n"
|
|
1529
|
-
"```mermaid\n"
|
|
1530
|
-
"graph TD\n"
|
|
1531
|
-
" A --> B\n"
|
|
1532
|
-
"```\n";
|
|
1533
|
-
html = apex_markdown_to_html(dl_para_fence, strlen(dl_para_fence), &opts);
|
|
1534
|
-
const char *para = strstr(html, "This should stay before the diagram");
|
|
1535
|
-
const char *pre = strstr(html, "<pre");
|
|
1536
|
-
test_result(para && pre && para < pre,
|
|
1537
|
-
"Paragraph between DL and fence stays before pre");
|
|
1538
|
-
apex_free_string(html);
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
/* DL + callout + following paragraph: blanks must not be eaten */
|
|
1542
|
-
{
|
|
1543
|
-
apex_options callout_opts = apex_options_default();
|
|
1544
|
-
callout_opts.mode = APEX_MODE_UNIFIED;
|
|
1545
|
-
callout_opts.enable_callouts = true;
|
|
1546
|
-
callout_opts.enable_definition_lists = true;
|
|
1547
|
-
const char *dl_callout =
|
|
1548
|
-
"Term\n"
|
|
1549
|
-
": Def\n"
|
|
1550
|
-
"\n"
|
|
1551
|
-
"> [!Note]\n"
|
|
1552
|
-
"> note body\n"
|
|
1553
|
-
"\n"
|
|
1554
|
-
"After callout\n";
|
|
1555
|
-
html = apex_markdown_to_html(dl_callout, strlen(dl_callout), &callout_opts);
|
|
1556
|
-
assert_contains(html, "callout", "Callout after DL still renders");
|
|
1557
|
-
assert_contains(html, "After callout", "Paragraph after callout preserved");
|
|
1558
|
-
/* "After callout" must not sit inside the callout content blockquote */
|
|
1559
|
-
const char *after = strstr(html, "After callout");
|
|
1560
|
-
const char *callout_end = strstr(html, "</div>\n</div>");
|
|
1561
|
-
if (!callout_end) callout_end = strstr(html, "callout-content");
|
|
1562
|
-
test_result(after != NULL, "After callout text present in output");
|
|
1563
|
-
if (after && strstr(html, "callout-content")) {
|
|
1564
|
-
/* Find the callout-content section and ensure After is outside it */
|
|
1565
|
-
const char *content = strstr(html, "callout-content");
|
|
1566
|
-
const char *content_close = content ? strstr(content, "</div>") : NULL;
|
|
1567
|
-
test_result(content_close && after > content_close,
|
|
1568
|
-
"After callout is outside callout-content");
|
|
1569
|
-
}
|
|
1570
|
-
apex_free_string(html);
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
bool had_failures = suite_end(suite_failures);
|
|
1574
|
-
print_suite_title("Definition Lists Tests", had_failures, false);
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
/**
|
|
1578
|
-
* Test advanced tables
|
|
1579
|
-
*/
|
|
1580
|
-
|
|
1581
|
-
void test_callouts(void) {
|
|
1582
|
-
int suite_failures = suite_start();
|
|
1583
|
-
print_suite_title("Callouts Tests", false, true);
|
|
1584
|
-
|
|
1585
|
-
apex_options opts = apex_options_default();
|
|
1586
|
-
opts.enable_callouts = true;
|
|
1587
|
-
char *html;
|
|
1588
|
-
|
|
1589
|
-
/* Test Bear/Obsidian NOTE callout */
|
|
1590
|
-
html = apex_markdown_to_html("> [!NOTE] Important\n> This is a note", 36, &opts);
|
|
1591
|
-
assert_contains(html, "class=\"callout", "Callout class present");
|
|
1592
|
-
assert_contains(html, "callout-note", "Note callout type");
|
|
1593
|
-
apex_free_string(html);
|
|
1594
|
-
|
|
1595
|
-
/* Test WARNING callout */
|
|
1596
|
-
html = apex_markdown_to_html("> [!WARNING] Be careful\n> Warning text", 38, &opts);
|
|
1597
|
-
assert_contains(html, "callout-warning", "Warning callout type");
|
|
1598
|
-
apex_free_string(html);
|
|
1599
|
-
|
|
1600
|
-
/* Test TIP callout */
|
|
1601
|
-
html = apex_markdown_to_html("> [!TIP] Pro tip\n> Helpful advice", 33, &opts);
|
|
1602
|
-
assert_contains(html, "callout-tip", "Tip callout type");
|
|
1603
|
-
apex_free_string(html);
|
|
1604
|
-
|
|
1605
|
-
/* Test DANGER callout */
|
|
1606
|
-
html = apex_markdown_to_html("> [!DANGER] Critical\n> Dangerous action", 40, &opts);
|
|
1607
|
-
assert_contains(html, "callout-danger", "Danger callout type");
|
|
1608
|
-
apex_free_string(html);
|
|
1609
|
-
|
|
1610
|
-
/* Test INFO callout */
|
|
1611
|
-
html = apex_markdown_to_html("> [!INFO] Information\n> Info text", 34, &opts);
|
|
1612
|
-
assert_contains(html, "callout-info", "Info callout type");
|
|
1613
|
-
apex_free_string(html);
|
|
1614
|
-
|
|
1615
|
-
/* Test collapsible callout with + */
|
|
1616
|
-
html = apex_markdown_to_html("> [!NOTE]+ Expandable\n> Content", 32, &opts);
|
|
1617
|
-
assert_contains(html, "<details", "Collapsible callout uses details");
|
|
1618
|
-
apex_free_string(html);
|
|
1619
|
-
|
|
1620
|
-
/* Test collapsed callout with - */
|
|
1621
|
-
html = apex_markdown_to_html("> [!NOTE]- Collapsed\n> Hidden content", 38, &opts);
|
|
1622
|
-
assert_contains(html, "<details", "Collapsed callout uses details");
|
|
1623
|
-
apex_free_string(html);
|
|
1624
|
-
|
|
1625
|
-
/* Test callout with multiple paragraphs */
|
|
1626
|
-
const char *multi = "> [!NOTE] Title\n> Para 1\n>\n> Para 2";
|
|
1627
|
-
html = apex_markdown_to_html(multi, strlen(multi), &opts);
|
|
1628
|
-
assert_contains(html, "callout", "Multi-paragraph callout");
|
|
1629
|
-
apex_free_string(html);
|
|
1630
|
-
|
|
1631
|
-
/* Test regular blockquote (not a callout) */
|
|
1632
|
-
html = apex_markdown_to_html("> Just a quote\n> Regular text", 29, &opts);
|
|
1633
|
-
if (strstr(html, "class=\"callout") == NULL) {
|
|
1634
|
-
test_result(true, "Regular blockquote not treated as callout");
|
|
1635
|
-
} else {
|
|
1636
|
-
test_result(false, "Regular blockquote incorrectly treated as callout");
|
|
1637
|
-
}
|
|
1638
|
-
apex_free_string(html);
|
|
1639
|
-
|
|
1640
|
-
/* Python-Markdown callouts are disabled by default */
|
|
1641
|
-
const char *py_callout = "!!! note \"Py Title\"\n Py body line";
|
|
1642
|
-
html = apex_markdown_to_html(py_callout, strlen(py_callout), &opts);
|
|
1643
|
-
assert_not_contains(html, "class=\"callout", "Python callout syntax ignored when flag disabled");
|
|
1644
|
-
apex_free_string(html);
|
|
1645
|
-
|
|
1646
|
-
/* Python-Markdown callouts enabled behind flag */
|
|
1647
|
-
opts.enable_py_callouts = true;
|
|
1648
|
-
html = apex_markdown_to_html(py_callout, strlen(py_callout), &opts);
|
|
1649
|
-
assert_contains(html, "class=\"callout", "Python callout recognized when enabled");
|
|
1650
|
-
assert_contains(html, "callout-note", "Python note callout type");
|
|
1651
|
-
assert_contains(html, "Py Title", "Python callout title preserved");
|
|
1652
|
-
apex_free_string(html);
|
|
1653
|
-
opts.enable_py_callouts = false;
|
|
1654
|
-
|
|
1655
|
-
/* markdown-callouts NOTE: syntax enabled behind py flag */
|
|
1656
|
-
opts.enable_py_callouts = true;
|
|
1657
|
-
html = apex_markdown_to_html("NOTE: Inline py callout body", 28, &opts);
|
|
1658
|
-
assert_contains(html, "class=\"callout", "NOTE: syntax recognized with py-callouts");
|
|
1659
|
-
assert_contains(html, "callout-note", "NOTE: maps to note callout");
|
|
1660
|
-
assert_contains(html, "Inline py callout body", "NOTE: body preserved");
|
|
1661
|
-
apex_free_string(html);
|
|
1662
|
-
|
|
1663
|
-
/* markdown-callouts collapsed syntax */
|
|
1664
|
-
const char *collapsed_py = ">? NOTE: Collapsed note\n> Collapsed body";
|
|
1665
|
-
html = apex_markdown_to_html(collapsed_py, strlen(collapsed_py), &opts);
|
|
1666
|
-
assert_contains(html, "<details", ">? syntax creates collapsible callout");
|
|
1667
|
-
assert_contains(html, "callout-note", ">? NOTE maps to note callout");
|
|
1668
|
-
assert_contains(html, "Collapsed body", ">? NOTE body preserved");
|
|
1669
|
-
apex_free_string(html);
|
|
1670
|
-
|
|
1671
|
-
opts.enable_py_callouts = false;
|
|
1672
|
-
|
|
1673
|
-
/* Quarto callouts are disabled by default */
|
|
1674
|
-
const char *quarto_callout =
|
|
1675
|
-
"::: {.callout-warning}\n"
|
|
1676
|
-
"## Quarto Title\n"
|
|
1677
|
-
"Quarto warning body.\n"
|
|
1678
|
-
":::\n";
|
|
1679
|
-
html = apex_markdown_to_html(quarto_callout, strlen(quarto_callout), &opts);
|
|
1680
|
-
assert_not_contains(html, "class=\"callout callout-warning\"", "Quarto callout syntax ignored when flag disabled");
|
|
1681
|
-
apex_free_string(html);
|
|
1682
|
-
|
|
1683
|
-
/* Quarto callouts enabled behind flag */
|
|
1684
|
-
opts.enable_quarto_callouts = true;
|
|
1685
|
-
html = apex_markdown_to_html(quarto_callout, strlen(quarto_callout), &opts);
|
|
1686
|
-
assert_contains(html, "class=\"callout", "Quarto callout recognized when enabled");
|
|
1687
|
-
assert_contains(html, "callout-warning", "Quarto warning callout type");
|
|
1688
|
-
assert_contains(html, "Quarto warning body", "Quarto callout body preserved");
|
|
1689
|
-
apex_free_string(html);
|
|
1690
|
-
|
|
1691
|
-
/* Quarto flag should still allow non-callout divs to render as divs */
|
|
1692
|
-
const char *regular_div =
|
|
1693
|
-
"::: {.sidebar}\n"
|
|
1694
|
-
"Regular div content.\n"
|
|
1695
|
-
":::\n";
|
|
1696
|
-
html = apex_markdown_to_html(regular_div, strlen(regular_div), &opts);
|
|
1697
|
-
assert_contains(html, "<div class=\"sidebar\"", "Non-callout fenced div still rendered with Quarto mode enabled");
|
|
1698
|
-
assert_not_contains(html, "class=\"callout", "Non-callout fenced div not converted into callout");
|
|
1699
|
-
apex_free_string(html);
|
|
1700
|
-
opts.enable_quarto_callouts = false;
|
|
1701
|
-
|
|
1702
|
-
bool had_failures = suite_end(suite_failures);
|
|
1703
|
-
print_suite_title("Callouts Tests", had_failures, false);
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
/**
|
|
1707
|
-
* Test blockquotes with lists
|
|
1708
|
-
*/
|
|
1709
|
-
|
|
1710
|
-
void test_blockquote_lists(void) {
|
|
1711
|
-
int suite_failures = suite_start();
|
|
1712
|
-
print_suite_title("Blockquote Lists Tests", false, true);
|
|
1713
|
-
|
|
1714
|
-
apex_options opts = apex_options_default();
|
|
1715
|
-
char *html;
|
|
1716
|
-
|
|
1717
|
-
/* Test unordered list in blockquote */
|
|
1718
|
-
html = apex_markdown_to_html("> Quote text\n>\n> - Item 1\n> - Item 2\n> - Item 3", 50, &opts);
|
|
1719
|
-
assert_contains(html, "<blockquote>", "Blockquote with list has blockquote tag");
|
|
1720
|
-
assert_contains(html, "<ul>", "Blockquote contains unordered list");
|
|
1721
|
-
assert_contains(html, "<li>Item 1</li>", "First list item in blockquote");
|
|
1722
|
-
assert_contains(html, "<li>Item 2</li>", "Second list item in blockquote");
|
|
1723
|
-
assert_contains(html, "<li>Item 3</li>", "Third list item in blockquote");
|
|
1724
|
-
apex_free_string(html);
|
|
1725
|
-
|
|
1726
|
-
/* Test ordered list in blockquote */
|
|
1727
|
-
const char *ordered_list = "> Numbered items:\n>\n> 1. First\n> 2. Second\n> 3. Third";
|
|
1728
|
-
html = apex_markdown_to_html(ordered_list, strlen(ordered_list), &opts);
|
|
1729
|
-
assert_contains(html, "<blockquote>", "Blockquote with ordered list");
|
|
1730
|
-
assert_contains(html, "<ol>", "Blockquote contains ordered list");
|
|
1731
|
-
assert_contains(html, "<li>First</li>", "First ordered item");
|
|
1732
|
-
assert_contains(html, "<li>Second</li>", "Second ordered item");
|
|
1733
|
-
assert_contains(html, "<li>Third</li>", "Third ordered item");
|
|
1734
|
-
apex_free_string(html);
|
|
1735
|
-
|
|
1736
|
-
/* Test nested list in blockquote */
|
|
1737
|
-
html = apex_markdown_to_html("> Main list:\n>\n> - Item 1\n> - Nested 1\n> - Nested 2\n> - Item 2", 60, &opts);
|
|
1738
|
-
assert_contains(html, "<blockquote>", "Blockquote with nested list");
|
|
1739
|
-
assert_contains(html, "<ul>", "Outer list present");
|
|
1740
|
-
assert_contains(html, "<li>Item 1", "Outer list item");
|
|
1741
|
-
assert_contains(html, "<li>Nested 1", "Nested list item");
|
|
1742
|
-
assert_contains(html, "<li>Nested 2", "Second nested item");
|
|
1743
|
-
apex_free_string(html);
|
|
1744
|
-
|
|
1745
|
-
/* Test list with paragraph in blockquote */
|
|
1746
|
-
const char *list_para = "> Introduction\n>\n> - Point one\n> - Point two\n>\n> Conclusion";
|
|
1747
|
-
html = apex_markdown_to_html(list_para, strlen(list_para), &opts);
|
|
1748
|
-
assert_contains(html, "<blockquote>", "Blockquote with list and paragraphs");
|
|
1749
|
-
assert_contains(html, "Introduction", "Paragraph before list");
|
|
1750
|
-
assert_contains(html, "<ul>", "List present");
|
|
1751
|
-
/* Conclusion may be in a separate blockquote or paragraph */
|
|
1752
|
-
assert_contains(html, "Conclusion", "Conclusion text present");
|
|
1753
|
-
apex_free_string(html);
|
|
1754
|
-
|
|
1755
|
-
/* Test task list in blockquote (requires GFM mode) */
|
|
1756
|
-
apex_options gfm_opts = apex_options_for_mode(APEX_MODE_GFM);
|
|
1757
|
-
const char *task_list = "> Tasks:\n>\n> - [ ] Todo\n> - [x] Done\n> - [ ] Another";
|
|
1758
|
-
html = apex_markdown_to_html(task_list, strlen(task_list), &gfm_opts);
|
|
1759
|
-
assert_contains(html, "<blockquote>", "Blockquote with task list");
|
|
1760
|
-
/* Task lists in blockquotes may not render checkboxes - verify content is present */
|
|
1761
|
-
assert_contains(html, "Todo", "Todo item");
|
|
1762
|
-
assert_contains(html, "Done", "Done item");
|
|
1763
|
-
apex_free_string(html);
|
|
1764
|
-
|
|
1765
|
-
/* Test definition list in blockquote (MMD mode) */
|
|
1766
|
-
html = apex_markdown_to_html("> Terms:\n>\n> Term 1\n> : Definition 1\n>\n> Term 2\n> : Definition 2", 60, &opts);
|
|
1767
|
-
assert_contains(html, "<blockquote>", "Blockquote with definition list");
|
|
1768
|
-
/* Definition lists may or may not be parsed depending on mode */
|
|
1769
|
-
apex_free_string(html);
|
|
1770
|
-
|
|
1771
|
-
/* Reference image after blockquote without blank line should not stay in blockquote */
|
|
1772
|
-
const char *ref_img_after_quote =
|
|
1773
|
-
"> Block quote\n"
|
|
1774
|
-
"![][2]\n"
|
|
1775
|
-
"\n"
|
|
1776
|
-
"[2]: /path/to/image.jpg\n"
|
|
1777
|
-
"\n"
|
|
1778
|
-
"Additional paragraph.";
|
|
1779
|
-
html = apex_markdown_to_html(ref_img_after_quote, strlen(ref_img_after_quote), &opts);
|
|
1780
|
-
assert_contains(html, "<blockquote>", "Blockquote present for ref image test");
|
|
1781
|
-
assert_contains(html, "Block quote", "Blockquote text preserved");
|
|
1782
|
-
assert_not_contains(html, "<blockquote>\n<p>Block quote\n<img", "Reference image not lazy-continued into blockquote");
|
|
1783
|
-
assert_contains(html, "<img src=\"/path/to/image.jpg\"", "Reference image resolved outside blockquote");
|
|
1784
|
-
assert_contains(html, "Additional paragraph.", "Paragraph after reference image preserved");
|
|
1785
|
-
apex_free_string(html);
|
|
1786
|
-
|
|
1787
|
-
/* Inline image after blockquote without blank line */
|
|
1788
|
-
html = apex_markdown_to_html("> Block quote\n\n\nNext paragraph.", 52, &opts);
|
|
1789
|
-
assert_not_contains(html, "<blockquote>\n<p>Block quote\n<", "Inline image not inside blockquote paragraph");
|
|
1790
|
-
assert_contains(html, "test.png", "Inline image rendered outside blockquote");
|
|
1791
|
-
apex_free_string(html);
|
|
1792
|
-
|
|
1793
|
-
bool had_failures = suite_end(suite_failures);
|
|
1794
|
-
print_suite_title("Blockquote Lists Tests", had_failures, false);
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
/**
|
|
1798
|
-
* Test TOC generation
|
|
1799
|
-
*/
|
|
1800
|
-
|
|
1801
|
-
void test_html_markdown_attributes(void) {
|
|
1802
|
-
int suite_failures = suite_start();
|
|
1803
|
-
print_suite_title("HTML Markdown Attributes Tests", false, true);
|
|
1804
|
-
|
|
1805
|
-
apex_options opts = apex_options_default();
|
|
1806
|
-
char *html;
|
|
1807
|
-
|
|
1808
|
-
/* Test markdown="1" (parse as block markdown) */
|
|
1809
|
-
const char *block1 = "<div markdown=\"1\">\n# Header\n\n**bold**\n</div>";
|
|
1810
|
-
html = apex_markdown_to_html(block1, strlen(block1), &opts);
|
|
1811
|
-
assert_contains(html, "<h1>Header</h1>", "markdown=\"1\" parses headers");
|
|
1812
|
-
assert_contains(html, "<strong>bold</strong>", "markdown=\"1\" parses emphasis");
|
|
1813
|
-
apex_free_string(html);
|
|
1814
|
-
|
|
1815
|
-
/* Test markdown="block" (parse as block markdown) */
|
|
1816
|
-
const char *block_attr = "<div markdown=\"block\">\n## Section\n\n- List item\n</div>";
|
|
1817
|
-
html = apex_markdown_to_html(block_attr, strlen(block_attr), &opts);
|
|
1818
|
-
assert_contains(html, "<h2>Section</h2>", "markdown=\"block\" parses headers");
|
|
1819
|
-
assert_contains(html, "<li>List item</li>", "markdown=\"block\" parses lists");
|
|
1820
|
-
apex_free_string(html);
|
|
1821
|
-
|
|
1822
|
-
/* Test markdown="span" (parse as inline markdown) */
|
|
1823
|
-
const char *span = "<div markdown=\"span\">**bold** and *italic*</div>";
|
|
1824
|
-
html = apex_markdown_to_html(span, strlen(span), &opts);
|
|
1825
|
-
assert_contains(html, "<strong>bold</strong>", "markdown=\"span\" parses bold");
|
|
1826
|
-
assert_contains(html, "<em>italic</em>", "markdown=\"span\" parses italic");
|
|
1827
|
-
apex_free_string(html);
|
|
1828
|
-
|
|
1829
|
-
/* Test markdown="0" (no processing) */
|
|
1830
|
-
const char *no_parse = "<div markdown=\"0\">\n**not bold**\n</div>";
|
|
1831
|
-
html = apex_markdown_to_html(no_parse, strlen(no_parse), &opts);
|
|
1832
|
-
assert_contains(html, "**not bold**", "markdown=\"0\" preserves literal text");
|
|
1833
|
-
apex_free_string(html);
|
|
1834
|
-
|
|
1835
|
-
/* Test nested HTML with markdown - nested tags may not parse */
|
|
1836
|
-
const char *nested = "<section markdown=\"1\">\n<div>\n# Nested Header\n</div>\n</section>";
|
|
1837
|
-
html = apex_markdown_to_html(nested, strlen(nested), &opts);
|
|
1838
|
-
// Note: Nested HTML processing may need refinement
|
|
1839
|
-
assert_contains(html, "<section>", "Section tag preserved");
|
|
1840
|
-
// assert_contains(html, "<h1>", "Nested HTML with markdown");
|
|
1841
|
-
apex_free_string(html);
|
|
1842
|
-
|
|
1843
|
-
/* Test HTML without markdown attribute (default behavior) */
|
|
1844
|
-
const char *no_attr = "<div>\n**should not parse**\n</div>";
|
|
1845
|
-
html = apex_markdown_to_html(no_attr, strlen(no_attr), &opts);
|
|
1846
|
-
// Without markdown attribute, HTML content is typically preserved
|
|
1847
|
-
assert_contains(html, "<div>", "HTML preserved without markdown attribute");
|
|
1848
|
-
apex_free_string(html);
|
|
1849
|
-
|
|
1850
|
-
/* Reference-style links in markdown="1" blocks resolve definitions from full document */
|
|
1851
|
-
const char *ref_in_block =
|
|
1852
|
-
"<blockquote class=\"tip\" markdown=\"1\">\n"
|
|
1853
|
-
"See the [extension page][ext] for details.\n"
|
|
1854
|
-
"</blockquote>\n\n"
|
|
1855
|
-
"[ext]: https://example.com/ext \"Extension page\"";
|
|
1856
|
-
html = apex_markdown_to_html(ref_in_block, strlen(ref_in_block), &opts);
|
|
1857
|
-
assert_contains(html, "<a href=\"https://example.com/ext\"", "markdown=\"1\" resolves reference-style links");
|
|
1858
|
-
assert_contains(html, "extension page</a>", "markdown=\"1\" reference link text preserved");
|
|
1859
|
-
apex_free_string(html);
|
|
1860
|
-
|
|
1861
|
-
/* Reference footnotes in markdown="1" blocks resolve definitions from full document */
|
|
1862
|
-
const char *fn_in_block =
|
|
1863
|
-
"<blockquote class=\"tip\" markdown=\"1\">\n"
|
|
1864
|
-
"See footnote[^note] here.\n"
|
|
1865
|
-
"</blockquote>\n\n"
|
|
1866
|
-
"[^note]: Footnote text.";
|
|
1867
|
-
html = apex_markdown_to_html(fn_in_block, strlen(fn_in_block), &opts);
|
|
1868
|
-
assert_contains(html, "class=\"footnote-ref\"", "markdown=\"1\" resolves reference footnotes");
|
|
1869
|
-
assert_contains(html, "href=\"#fn-note\"", "markdown=\"1\" footnote links to block anchor");
|
|
1870
|
-
assert_contains(html, "<section class=\"footnotes\"", "markdown=\"1\" footnotes section rendered in block");
|
|
1871
|
-
assert_contains(html, "Footnote text.", "markdown=\"1\" footnote definition rendered in block");
|
|
1872
|
-
assert_not_contains(html, "See footnote[^note]", "markdown=\"1\" footnote not left literal");
|
|
1873
|
-
apex_free_string(html);
|
|
1874
|
-
|
|
1875
|
-
bool had_failures = suite_end(suite_failures);
|
|
1876
|
-
print_suite_title("HTML Markdown Attributes Tests", had_failures, false);
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
/**
|
|
1880
|
-
* Test Pandoc fenced divs
|
|
1881
|
-
*/
|
|
1882
|
-
|
|
1883
|
-
void test_fenced_divs(void) {
|
|
1884
|
-
int suite_failures = suite_start();
|
|
1885
|
-
print_suite_title("Fenced Divs Tests", false, true);
|
|
1886
|
-
|
|
1887
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
1888
|
-
opts.enable_divs = true;
|
|
1889
|
-
char *html;
|
|
1890
|
-
|
|
1891
|
-
/* Test basic fenced div with ID and class */
|
|
1892
|
-
const char *basic_div = "::::: {#special .sidebar}\nHere is a paragraph.\n\nAnd another.\n:::::";
|
|
1893
|
-
html = apex_markdown_to_html(basic_div, strlen(basic_div), &opts);
|
|
1894
|
-
assert_contains(html, "<div", "Basic fenced div renders");
|
|
1895
|
-
assert_contains(html, "id=\"special\"", "Fenced div has ID");
|
|
1896
|
-
assert_contains(html, "class=\"sidebar\"", "Fenced div has class");
|
|
1897
|
-
assert_contains(html, "Here is a paragraph", "Fenced div content preserved");
|
|
1898
|
-
assert_contains(html, "</div>", "Fenced div properly closed");
|
|
1899
|
-
apex_free_string(html);
|
|
1900
|
-
|
|
1901
|
-
/* Test fenced div with single unbraced word (treated as class) */
|
|
1902
|
-
const char *unbraced_class = "::: sidebar\nThis is a div.\n:::";
|
|
1903
|
-
html = apex_markdown_to_html(unbraced_class, strlen(unbraced_class), &opts);
|
|
1904
|
-
assert_contains(html, "<div", "Unbraced class div renders");
|
|
1905
|
-
assert_contains(html, "class=\"sidebar\"", "Unbraced word becomes class");
|
|
1906
|
-
apex_free_string(html);
|
|
1907
|
-
|
|
1908
|
-
/* Test fenced div with multiple classes */
|
|
1909
|
-
const char *multiple_classes = "::::: {.warning .important .highlight}\nWarning text\n:::::";
|
|
1910
|
-
html = apex_markdown_to_html(multiple_classes, strlen(multiple_classes), &opts);
|
|
1911
|
-
assert_contains(html, "<div", "Multiple classes div renders");
|
|
1912
|
-
assert_contains(html, "class=\"warning important highlight\"", "Multiple classes applied");
|
|
1913
|
-
apex_free_string(html);
|
|
1914
|
-
|
|
1915
|
-
/* Test fenced div with custom attributes */
|
|
1916
|
-
const char *custom_attrs = "::::: {#mydiv .container key=\"value\" data-id=\"123\"}\nContent\n:::::";
|
|
1917
|
-
html = apex_markdown_to_html(custom_attrs, strlen(custom_attrs), &opts);
|
|
1918
|
-
assert_contains(html, "<div", "Custom attributes div renders");
|
|
1919
|
-
assert_contains(html, "id=\"mydiv\"", "Custom attributes div has ID");
|
|
1920
|
-
assert_contains(html, "class=\"container\"", "Custom attributes div has class");
|
|
1921
|
-
assert_contains(html, "key=\"value\"", "Custom attribute key present");
|
|
1922
|
-
assert_contains(html, "data-id=\"123\"", "Custom attribute data-id present");
|
|
1923
|
-
apex_free_string(html);
|
|
1924
|
-
|
|
1925
|
-
/* Test fenced div with trailing colons */
|
|
1926
|
-
const char *trailing_colons = "::::: {#special .sidebar} ::::\nContent\n::::::::::::::::::";
|
|
1927
|
-
html = apex_markdown_to_html(trailing_colons, strlen(trailing_colons), &opts);
|
|
1928
|
-
assert_contains(html, "<div", "Trailing colons div renders");
|
|
1929
|
-
assert_contains(html, "id=\"special\"", "Trailing colons div has ID");
|
|
1930
|
-
apex_free_string(html);
|
|
1931
|
-
|
|
1932
|
-
/* Test nested fenced divs */
|
|
1933
|
-
const char *nested_divs = "::: Warning ::::::\nOuter warning.\n\n::: Danger\nInner danger.\n:::\n::::::::::::::::::";
|
|
1934
|
-
html = apex_markdown_to_html(nested_divs, strlen(nested_divs), &opts);
|
|
1935
|
-
assert_contains(html, "<div", "Nested divs render");
|
|
1936
|
-
assert_contains(html, "class=\"Warning\"", "Outer div class");
|
|
1937
|
-
assert_contains(html, "class=\"Danger\"", "Inner div class");
|
|
1938
|
-
assert_contains(html, "Outer warning", "Outer div content");
|
|
1939
|
-
assert_contains(html, "Inner danger", "Inner div content");
|
|
1940
|
-
/* Should have two opening divs and two closing divs */
|
|
1941
|
-
size_t open_count = 0, close_count = 0;
|
|
1942
|
-
const char *p = html;
|
|
1943
|
-
while ((p = strstr(p, "<div")) != NULL) {
|
|
1944
|
-
open_count++;
|
|
1945
|
-
p += 4;
|
|
1946
|
-
}
|
|
1947
|
-
p = html;
|
|
1948
|
-
while ((p = strstr(p, "</div>")) != NULL) {
|
|
1949
|
-
close_count++;
|
|
1950
|
-
p += 6;
|
|
1951
|
-
}
|
|
1952
|
-
if (open_count >= 2 && close_count >= 2) {
|
|
1953
|
-
tests_run++;
|
|
1954
|
-
tests_passed++;
|
|
1955
|
-
if (!errors_only_output) {
|
|
1956
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Nested divs properly structured\n");
|
|
1957
|
-
}
|
|
1958
|
-
} else {
|
|
1959
|
-
tests_run++;
|
|
1960
|
-
tests_failed++;
|
|
1961
|
-
printf(COLOR_RED "✗" COLOR_RESET " Nested divs properly structured\n");
|
|
1962
|
-
}
|
|
1963
|
-
apex_free_string(html);
|
|
1964
|
-
|
|
1965
|
-
/* Test fenced div with block type (aside) */
|
|
1966
|
-
const char *aside_block = "::: >aside {.sidebar}\nThis is an aside block.\n:::";
|
|
1967
|
-
html = apex_markdown_to_html(aside_block, strlen(aside_block), &opts);
|
|
1968
|
-
assert_contains(html, "<aside", "Aside block renders");
|
|
1969
|
-
assert_contains(html, "</aside>", "Aside block closes");
|
|
1970
|
-
assert_contains(html, "class=\"sidebar\"", "Aside block has class");
|
|
1971
|
-
assert_contains(html, "This is an aside block", "Aside block content");
|
|
1972
|
-
apex_free_string(html);
|
|
1973
|
-
|
|
1974
|
-
/* Test fenced div with block type (article) */
|
|
1975
|
-
const char *article_block = "::: >article {#post .main}\nArticle content here.\n:::";
|
|
1976
|
-
html = apex_markdown_to_html(article_block, strlen(article_block), &opts);
|
|
1977
|
-
assert_contains(html, "<article", "Article block renders");
|
|
1978
|
-
assert_contains(html, "</article>", "Article block closes");
|
|
1979
|
-
assert_contains(html, "id=\"post\"", "Article block has ID");
|
|
1980
|
-
assert_contains(html, "class=\"main\"", "Article block has class");
|
|
1981
|
-
apex_free_string(html);
|
|
1982
|
-
|
|
1983
|
-
/* Test fenced div with block type (details/summary - nested) */
|
|
1984
|
-
const char *details_block = "::: >details {.warning} :::\n:::: >summary\nThis is a summary\n::::\nThis is the content of the details block\n:::";
|
|
1985
|
-
html = apex_markdown_to_html(details_block, strlen(details_block), &opts);
|
|
1986
|
-
assert_contains(html, "<details", "Details block renders");
|
|
1987
|
-
assert_contains(html, "</details>", "Details block closes");
|
|
1988
|
-
assert_contains(html, "<summary", "Summary block renders");
|
|
1989
|
-
assert_contains(html, "</summary>", "Summary block closes");
|
|
1990
|
-
assert_contains(html, "class=\"warning\"", "Details block has class");
|
|
1991
|
-
assert_contains(html, "This is a summary", "Summary content");
|
|
1992
|
-
assert_contains(html, "This is the content of the details block", "Details content");
|
|
1993
|
-
apex_free_string(html);
|
|
1994
|
-
|
|
1995
|
-
/* Test fenced div with block type and unbraced class */
|
|
1996
|
-
const char *aside_unbraced = "::: >aside Warning :::\nWarning content.\n:::";
|
|
1997
|
-
html = apex_markdown_to_html(aside_unbraced, strlen(aside_unbraced), &opts);
|
|
1998
|
-
assert_contains(html, "<aside", "Aside with unbraced class renders");
|
|
1999
|
-
assert_contains(html, "class=\"Warning\"", "Aside has unbraced class");
|
|
2000
|
-
apex_free_string(html);
|
|
2001
|
-
|
|
2002
|
-
/* Test default div behavior (no > prefix) */
|
|
2003
|
-
const char *default_div = "::: {.container}\nRegular div content.\n:::";
|
|
2004
|
-
html = apex_markdown_to_html(default_div, strlen(default_div), &opts);
|
|
2005
|
-
assert_contains(html, "<div", "Default div renders");
|
|
2006
|
-
assert_contains(html, "</div>", "Default div closes");
|
|
2007
|
-
assert_contains(html, "class=\"container\"", "Default div has class");
|
|
2008
|
-
apex_free_string(html);
|
|
2009
|
-
|
|
2010
|
-
/* Test nested blocks with different types */
|
|
2011
|
-
const char *nested_blocks = "::: >section {.outer} :::\nOuter section.\n\n::: >aside {.inner}\nInner aside.\n:::\n\nMore outer content.\n:::";
|
|
2012
|
-
html = apex_markdown_to_html(nested_blocks, strlen(nested_blocks), &opts);
|
|
2013
|
-
assert_contains(html, "<section", "Nested section renders");
|
|
2014
|
-
assert_contains(html, "</section>", "Nested section closes");
|
|
2015
|
-
assert_contains(html, "<aside", "Nested aside renders");
|
|
2016
|
-
assert_contains(html, "</aside>", "Nested aside closes");
|
|
2017
|
-
assert_contains(html, "Outer section", "Outer content");
|
|
2018
|
-
assert_contains(html, "Inner aside", "Inner content");
|
|
2019
|
-
apex_free_string(html);
|
|
2020
|
-
|
|
2021
|
-
/* Test block type with section */
|
|
2022
|
-
const char *section_block = "::: >section {#chapter1 .main-section}\nSection content here.\n:::";
|
|
2023
|
-
html = apex_markdown_to_html(section_block, strlen(section_block), &opts);
|
|
2024
|
-
assert_contains(html, "<section", "Section block renders");
|
|
2025
|
-
assert_contains(html, "</section>", "Section block closes");
|
|
2026
|
-
assert_contains(html, "id=\"chapter1\"", "Section block has ID");
|
|
2027
|
-
assert_contains(html, "class=\"main-section\"", "Section block has class");
|
|
2028
|
-
apex_free_string(html);
|
|
2029
|
-
|
|
2030
|
-
/* Test block type with header */
|
|
2031
|
-
const char *header_block = "::: >header {.site-header}\nSite header content\n:::";
|
|
2032
|
-
html = apex_markdown_to_html(header_block, strlen(header_block), &opts);
|
|
2033
|
-
assert_contains(html, "<header", "Header block renders");
|
|
2034
|
-
assert_contains(html, "</header>", "Header block closes");
|
|
2035
|
-
apex_free_string(html);
|
|
2036
|
-
|
|
2037
|
-
/* Test block type with footer */
|
|
2038
|
-
const char *footer_block = "::: >footer {.site-footer}\nSite footer content\n:::";
|
|
2039
|
-
html = apex_markdown_to_html(footer_block, strlen(footer_block), &opts);
|
|
2040
|
-
assert_contains(html, "<footer", "Footer block renders");
|
|
2041
|
-
assert_contains(html, "</footer>", "Footer block closes");
|
|
2042
|
-
apex_free_string(html);
|
|
2043
|
-
|
|
2044
|
-
/* Test block type with nav */
|
|
2045
|
-
const char *nav_block = "::: >nav {.main-nav}\nNavigation content\n:::";
|
|
2046
|
-
html = apex_markdown_to_html(nav_block, strlen(nav_block), &opts);
|
|
2047
|
-
assert_contains(html, "<nav", "Nav block renders");
|
|
2048
|
-
assert_contains(html, "</nav>", "Nav block closes");
|
|
2049
|
-
apex_free_string(html);
|
|
2050
|
-
|
|
2051
|
-
/* Test block type with explicit div */
|
|
2052
|
-
const char *explicit_div = "::: >div {.custom-div}\nExplicit div content\n:::";
|
|
2053
|
-
html = apex_markdown_to_html(explicit_div, strlen(explicit_div), &opts);
|
|
2054
|
-
assert_contains(html, "<div", "Explicit div block renders");
|
|
2055
|
-
assert_contains(html, "</div>", "Explicit div block closes");
|
|
2056
|
-
apex_free_string(html);
|
|
2057
|
-
|
|
2058
|
-
/* Test block type with trailing colons */
|
|
2059
|
-
const char *block_trailing = "::: >aside {.sidebar} :::\nContent with trailing colons\n:::";
|
|
2060
|
-
html = apex_markdown_to_html(block_trailing, strlen(block_trailing), &opts);
|
|
2061
|
-
assert_contains(html, "<aside", "Block with trailing colons renders");
|
|
2062
|
-
assert_contains(html, "class=\"sidebar\"", "Block with trailing colons has class");
|
|
2063
|
-
apex_free_string(html);
|
|
2064
|
-
|
|
2065
|
-
/* Test block type with multiple attributes */
|
|
2066
|
-
const char *block_multi_attr = "::: >article {#post .main .highlight data-id=\"123\" role=\"main\"}\nArticle with multiple attributes\n:::";
|
|
2067
|
-
html = apex_markdown_to_html(block_multi_attr, strlen(block_multi_attr), &opts);
|
|
2068
|
-
assert_contains(html, "<article", "Block with multiple attributes renders");
|
|
2069
|
-
assert_contains(html, "id=\"post\"", "Block has ID");
|
|
2070
|
-
assert_contains(html, "class=\"main highlight\"", "Block has multiple classes");
|
|
2071
|
-
assert_contains(html, "data-id=\"123\"", "Block has data attribute");
|
|
2072
|
-
assert_contains(html, "role=\"main\"", "Block has role attribute");
|
|
2073
|
-
apex_free_string(html);
|
|
2074
|
-
|
|
2075
|
-
/* Test deeply nested block types */
|
|
2076
|
-
const char *deep_nested = "::: >section {.level1} :::\nLevel 1\n\n::: >article {.level2}\nLevel 2\n\n::: >aside {.level3}\nLevel 3\n:::\n\nMore level 2\n:::\n\nMore level 1\n:::";
|
|
2077
|
-
html = apex_markdown_to_html(deep_nested, strlen(deep_nested), &opts);
|
|
2078
|
-
assert_contains(html, "<section", "Deep nested section renders");
|
|
2079
|
-
assert_contains(html, "</section>", "Deep nested section closes");
|
|
2080
|
-
assert_contains(html, "<article", "Deep nested article renders");
|
|
2081
|
-
assert_contains(html, "</article>", "Deep nested article closes");
|
|
2082
|
-
assert_contains(html, "<aside", "Deep nested aside renders");
|
|
2083
|
-
assert_contains(html, "</aside>", "Deep nested aside closes");
|
|
2084
|
-
assert_contains(html, "Level 1", "Deep nested level 1 content");
|
|
2085
|
-
assert_contains(html, "Level 2", "Deep nested level 2 content");
|
|
2086
|
-
assert_contains(html, "Level 3", "Deep nested level 3 content");
|
|
2087
|
-
apex_free_string(html);
|
|
2088
|
-
|
|
2089
|
-
/* Test mixed block types and regular divs */
|
|
2090
|
-
const char *mixed_types = "::: >section {.outer}\nSection content\n\n::: {.regular-div}\nRegular div inside section\n:::\n\n::: >aside {.aside-in-section}\nAside inside section\n:::\n\nMore section content\n:::";
|
|
2091
|
-
html = apex_markdown_to_html(mixed_types, strlen(mixed_types), &opts);
|
|
2092
|
-
assert_contains(html, "<section", "Mixed types section renders");
|
|
2093
|
-
assert_contains(html, "</section>", "Mixed types section closes");
|
|
2094
|
-
assert_contains(html, "<div", "Mixed types regular div renders");
|
|
2095
|
-
assert_contains(html, "</div>", "Mixed types regular div closes");
|
|
2096
|
-
assert_contains(html, "<aside", "Mixed types aside renders");
|
|
2097
|
-
assert_contains(html, "</aside>", "Mixed types aside closes");
|
|
2098
|
-
apex_free_string(html);
|
|
2099
|
-
|
|
2100
|
-
/* Test block type with hyphenated name */
|
|
2101
|
-
const char *hyphenated = "::: >custom-element {.test}\nCustom element content\n:::";
|
|
2102
|
-
html = apex_markdown_to_html(hyphenated, strlen(hyphenated), &opts);
|
|
2103
|
-
assert_contains(html, "<custom-element", "Hyphenated block type renders");
|
|
2104
|
-
assert_contains(html, "</custom-element>", "Hyphenated block type closes");
|
|
2105
|
-
apex_free_string(html);
|
|
2106
|
-
|
|
2107
|
-
/* Test block type preserves markdown parsing */
|
|
2108
|
-
const char *block_with_markdown = "::: >article {.content}\n## Heading\n\nParagraph with **bold** text.\n:::";
|
|
2109
|
-
html = apex_markdown_to_html(block_with_markdown, strlen(block_with_markdown), &opts);
|
|
2110
|
-
assert_contains(html, "<article", "Block with markdown renders");
|
|
2111
|
-
assert_contains(html, "<h2", "Block content parsed as markdown (heading)");
|
|
2112
|
-
assert_contains(html, "Heading", "Block content parsed as markdown (heading text)");
|
|
2113
|
-
assert_contains(html, "<strong", "Block content parsed as markdown (bold)");
|
|
2114
|
-
assert_contains(html, "bold", "Block content parsed as markdown (bold text)");
|
|
2115
|
-
apex_free_string(html);
|
|
2116
|
-
|
|
2117
|
-
/* Test minimum 3 colons */
|
|
2118
|
-
const char *min_colons = "::: {.minimal}\nMinimal div\n:::";
|
|
2119
|
-
html = apex_markdown_to_html(min_colons, strlen(min_colons), &opts);
|
|
2120
|
-
assert_contains(html, "<div", "Minimum colons div renders");
|
|
2121
|
-
assert_contains(html, "class=\"minimal\"", "Minimum colons div has class");
|
|
2122
|
-
apex_free_string(html);
|
|
2123
|
-
|
|
2124
|
-
/* Test fenced div with only ID */
|
|
2125
|
-
const char *only_id = "::: {#only-id}\nDiv with only ID\n:::";
|
|
2126
|
-
html = apex_markdown_to_html(only_id, strlen(only_id), &opts);
|
|
2127
|
-
assert_contains(html, "<div", "Only ID div renders");
|
|
2128
|
-
assert_contains(html, "id=\"only-id\"", "Only ID div has ID");
|
|
2129
|
-
assert_not_contains(html, "class=", "Only ID div has no class");
|
|
2130
|
-
apex_free_string(html);
|
|
2131
|
-
|
|
2132
|
-
/* Test fenced div with only classes */
|
|
2133
|
-
const char *only_classes = "::: {.only-classes .multiple}\nDiv with only classes\n:::";
|
|
2134
|
-
html = apex_markdown_to_html(only_classes, strlen(only_classes), &opts);
|
|
2135
|
-
assert_contains(html, "<div", "Only classes div renders");
|
|
2136
|
-
assert_contains(html, "class=\"only-classes multiple\"", "Only classes div has classes");
|
|
2137
|
-
assert_not_contains(html, "id=", "Only classes div has no ID");
|
|
2138
|
-
apex_free_string(html);
|
|
2139
|
-
|
|
2140
|
-
/* Test fenced div with quoted attribute values */
|
|
2141
|
-
const char *quoted_values = "::::: {#quoted .test attr1=\"quoted value\" attr2='single quoted'}\nContent\n:::::";
|
|
2142
|
-
html = apex_markdown_to_html(quoted_values, strlen(quoted_values), &opts);
|
|
2143
|
-
assert_contains(html, "<div", "Quoted values div renders");
|
|
2144
|
-
assert_contains(html, "attr1=\"quoted value\"", "Double-quoted attribute");
|
|
2145
|
-
assert_contains(html, "attr2=\"single quoted\"", "Single-quoted attribute converted");
|
|
2146
|
-
apex_free_string(html);
|
|
2147
|
-
|
|
2148
|
-
/* Test fenced div disabled in non-Unified mode */
|
|
2149
|
-
apex_options gfm_opts = apex_options_for_mode(APEX_MODE_GFM);
|
|
2150
|
-
gfm_opts.enable_divs = true; /* Even if enabled, should not work in GFM mode */
|
|
2151
|
-
const char *div_in_gfm = "::: {.test}\nContent\n:::";
|
|
2152
|
-
html = apex_markdown_to_html(div_in_gfm, strlen(div_in_gfm), &gfm_opts);
|
|
2153
|
-
assert_not_contains(html, "<div", "Fenced divs disabled in GFM mode");
|
|
2154
|
-
apex_free_string(html);
|
|
2155
|
-
|
|
2156
|
-
/* Test fenced div disabled with --no-divs flag */
|
|
2157
|
-
apex_options no_divs_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
2158
|
-
no_divs_opts.enable_divs = false;
|
|
2159
|
-
const char *div_disabled = "::: {.test}\nContent\n:::";
|
|
2160
|
-
html = apex_markdown_to_html(div_disabled, strlen(div_disabled), &no_divs_opts);
|
|
2161
|
-
assert_not_contains(html, "<div", "Fenced divs disabled with --no-divs");
|
|
2162
|
-
apex_free_string(html);
|
|
2163
|
-
|
|
2164
|
-
/* Test fenced div with mixed content (lists, blockquotes) */
|
|
2165
|
-
const char *mixed_content = "::::: {#mixed .content}\n- List item\n- Another item\n\n> A blockquote\n\nAnd a paragraph.\n:::::";
|
|
2166
|
-
html = apex_markdown_to_html(mixed_content, strlen(mixed_content), &opts);
|
|
2167
|
-
assert_contains(html, "<div", "Mixed content div renders");
|
|
2168
|
-
assert_contains(html, "<ul>", "Mixed content has list");
|
|
2169
|
-
assert_contains(html, "<blockquote>", "Mixed content has blockquote");
|
|
2170
|
-
assert_contains(html, "<p>And a paragraph.</p>", "Mixed content has paragraph");
|
|
2171
|
-
apex_free_string(html);
|
|
2172
|
-
|
|
2173
|
-
/* Test multiple fenced divs in sequence */
|
|
2174
|
-
const char *multiple_divs = "::: {.first}\nFirst div.\n:::\n\n::: {.second}\nSecond div.\n:::\n\n::: {.third}\nThird div.\n:::";
|
|
2175
|
-
html = apex_markdown_to_html(multiple_divs, strlen(multiple_divs), &opts);
|
|
2176
|
-
assert_contains(html, "class=\"first\"", "First div class");
|
|
2177
|
-
assert_contains(html, "class=\"second\"", "Second div class");
|
|
2178
|
-
assert_contains(html, "class=\"third\"", "Third div class");
|
|
2179
|
-
assert_contains(html, "First div", "First div content");
|
|
2180
|
-
assert_contains(html, "Second div", "Second div content");
|
|
2181
|
-
assert_contains(html, "Third div", "Third div content");
|
|
2182
|
-
apex_free_string(html);
|
|
2183
|
-
|
|
2184
|
-
bool had_failures = suite_end(suite_failures);
|
|
2185
|
-
print_suite_title("Fenced Divs Tests", had_failures, false);
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
/**
|
|
2189
|
-
* Test abbreviations
|
|
2190
|
-
*/
|
|
2191
|
-
|
|
2192
|
-
void test_abbreviations(void) {
|
|
2193
|
-
int suite_failures = suite_start();
|
|
2194
|
-
print_suite_title("Abbreviations Tests", false, true);
|
|
2195
|
-
|
|
2196
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
2197
|
-
char *html;
|
|
2198
|
-
|
|
2199
|
-
/* Test basic abbreviation */
|
|
2200
|
-
const char *abbr_doc = "*[HTML]: Hypertext Markup Language\n\nHTML is great.";
|
|
2201
|
-
html = apex_markdown_to_html(abbr_doc, strlen(abbr_doc), &opts);
|
|
2202
|
-
assert_contains(html, "<abbr", "Abbreviation tag created");
|
|
2203
|
-
assert_contains(html, "Hypertext Markup Language", "Abbreviation title");
|
|
2204
|
-
apex_free_string(html);
|
|
2205
|
-
|
|
2206
|
-
/* Test multiple abbreviations */
|
|
2207
|
-
const char *multi_abbr = "*[CSS]: Cascading Style Sheets\n*[JS]: JavaScript\n\nCSS and JS are essential.";
|
|
2208
|
-
html = apex_markdown_to_html(multi_abbr, strlen(multi_abbr), &opts);
|
|
2209
|
-
assert_contains(html, "<abbr", "Abbreviation tags present");
|
|
2210
|
-
assert_contains(html, "Cascading Style Sheets", "First abbreviation");
|
|
2211
|
-
assert_contains(html, "JavaScript", "Second abbreviation");
|
|
2212
|
-
apex_free_string(html);
|
|
2213
|
-
|
|
2214
|
-
/* Test abbreviation with multiple occurrences */
|
|
2215
|
-
const char *multiple = "*[API]: Application Programming Interface\n\nThe API docs explain the API usage.";
|
|
2216
|
-
html = apex_markdown_to_html(multiple, strlen(multiple), &opts);
|
|
2217
|
-
assert_contains(html, "<abbr", "Multiple occurrences wrapped");
|
|
2218
|
-
assert_contains(html, "Application Programming Interface", "Abbreviation definition");
|
|
2219
|
-
apex_free_string(html);
|
|
2220
|
-
|
|
2221
|
-
/* Test text without abbreviations */
|
|
2222
|
-
const char *no_abbr = "Just plain text here.";
|
|
2223
|
-
html = apex_markdown_to_html(no_abbr, strlen(no_abbr), &opts);
|
|
2224
|
-
assert_contains(html, "plain text", "Non-abbreviation text preserved");
|
|
2225
|
-
apex_free_string(html);
|
|
2226
|
-
|
|
2227
|
-
/* Test MMD 6 reference abbreviation: [>abbr]: expansion */
|
|
2228
|
-
const char *mmd6_ref = "[>MMD]: MultiMarkdown\n\n[>MMD] is great.";
|
|
2229
|
-
html = apex_markdown_to_html(mmd6_ref, strlen(mmd6_ref), &opts);
|
|
2230
|
-
assert_contains(html, "<abbr", "MMD 6 reference abbr tag");
|
|
2231
|
-
assert_contains(html, "MultiMarkdown", "MMD 6 reference expansion");
|
|
2232
|
-
apex_free_string(html);
|
|
2233
|
-
|
|
2234
|
-
/* Test MMD 6 inline abbreviation: [>(abbr) expansion] */
|
|
2235
|
-
const char *mmd6_inline = "This is [>(MD) Markdown] syntax.";
|
|
2236
|
-
html = apex_markdown_to_html(mmd6_inline, strlen(mmd6_inline), &opts);
|
|
2237
|
-
assert_contains(html, "<abbr title=\"Markdown\">MD</abbr>", "MMD 6 inline abbr");
|
|
2238
|
-
apex_free_string(html);
|
|
2239
|
-
|
|
2240
|
-
/* Test multiple MMD 6 inline abbreviations */
|
|
2241
|
-
const char *mmd6_multi = "[>(HTML) Hypertext] and [>(CSS) Styles] work.";
|
|
2242
|
-
html = apex_markdown_to_html(mmd6_multi, strlen(mmd6_multi), &opts);
|
|
2243
|
-
assert_contains(html, "title=\"Hypertext\">HTML</abbr>", "First MMD 6 inline");
|
|
2244
|
-
assert_contains(html, "title=\"Styles\">CSS</abbr>", "Second MMD 6 inline");
|
|
2245
|
-
apex_free_string(html);
|
|
2246
|
-
|
|
2247
|
-
/* Test mixing old and new syntax */
|
|
2248
|
-
const char *mixed = "*[OLD]: Old Style\n[>NEW]: New Style\n\nOLD and [>NEW] work.";
|
|
2249
|
-
html = apex_markdown_to_html(mixed, strlen(mixed), &opts);
|
|
2250
|
-
assert_contains(html, "Old Style", "Old syntax in mixed");
|
|
2251
|
-
assert_contains(html, "New Style", "New syntax in mixed");
|
|
2252
|
-
apex_free_string(html);
|
|
2253
|
-
|
|
2254
|
-
/* Test inline and reference-style in same document (both must be wrapped) */
|
|
2255
|
-
const char *inline_and_ref = "This is HTML. And more [>(ABBR) abbreviation syntax].\n\n[>HTML]: Hypertext Markup Language";
|
|
2256
|
-
html = apex_markdown_to_html(inline_and_ref, strlen(inline_and_ref), &opts);
|
|
2257
|
-
assert_contains(html, "<abbr title=\"Hypertext Markup Language\">HTML</abbr>", "Reference abbr when mixed");
|
|
2258
|
-
assert_contains(html, "<abbr title=\"abbreviation syntax\">ABBR</abbr>", "Inline abbr when mixed");
|
|
2259
|
-
apex_free_string(html);
|
|
2260
|
-
|
|
2261
|
-
/* Regression for issue #31: preprocessing must preserve physical lines
|
|
2262
|
-
* longer than the old 1024-byte abbreviation and 2048-byte ALD buffers. */
|
|
2263
|
-
const char *tail_marker = "END-OF-LONG-LINE";
|
|
2264
|
-
const size_t prefix_len = 2160;
|
|
2265
|
-
size_t marker_len = strlen(tail_marker);
|
|
2266
|
-
char *long_line = malloc(prefix_len + marker_len + 2);
|
|
2267
|
-
if (long_line) {
|
|
2268
|
-
memset(long_line, 'a', prefix_len);
|
|
2269
|
-
memcpy(long_line + prefix_len, tail_marker, marker_len);
|
|
2270
|
-
long_line[prefix_len + marker_len] = '\n';
|
|
2271
|
-
long_line[prefix_len + marker_len + 1] = '\0';
|
|
2272
|
-
|
|
2273
|
-
const apex_mode_t modes[] = {
|
|
2274
|
-
APEX_MODE_MULTIMARKDOWN,
|
|
2275
|
-
APEX_MODE_KRAMDOWN,
|
|
2276
|
-
APEX_MODE_UNIFIED,
|
|
2277
|
-
APEX_MODE_QUARTO
|
|
2278
|
-
};
|
|
2279
|
-
const char *mode_names[] = {"MMD", "Kramdown", "Unified", "Quarto"};
|
|
2280
|
-
for (size_t i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
|
|
2281
|
-
apex_options long_opts = apex_options_for_mode(modes[i]);
|
|
2282
|
-
html = apex_markdown_to_html(long_line, strlen(long_line), &long_opts);
|
|
2283
|
-
test_resultf(html && strstr(html, tail_marker) != NULL,
|
|
2284
|
-
"%s mode preserves lines longer than 2048 bytes", mode_names[i]);
|
|
2285
|
-
apex_free_string(html);
|
|
2286
|
-
}
|
|
2287
|
-
free(long_line);
|
|
2288
|
-
} else {
|
|
2289
|
-
test_result(false, "Allocate issue #31 long-line regression input");
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
bool had_failures = suite_end(suite_failures);
|
|
2293
|
-
print_suite_title("Abbreviations Tests", had_failures, false);
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
/**
|
|
2297
|
-
* Test MMD 6 features: multi-line setext headers and link/image titles with different quotes
|
|
2298
|
-
*/
|
|
2299
|
-
|
|
2300
|
-
void test_mmd6_features(void) {
|
|
2301
|
-
int suite_failures = suite_start();
|
|
2302
|
-
print_suite_title("MMD 6 Features Tests", false, true);
|
|
2303
|
-
|
|
2304
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
2305
|
-
char *html;
|
|
2306
|
-
|
|
2307
|
-
/* Test multi-line setext header (h1) */
|
|
2308
|
-
const char *multiline_h1 = "This is\na multi-line\nsetext header\n========";
|
|
2309
|
-
html = apex_markdown_to_html(multiline_h1, strlen(multiline_h1), &opts);
|
|
2310
|
-
assert_contains(html, "<h1", "Multi-line setext h1 tag");
|
|
2311
|
-
assert_contains(html, "This is", "Multi-line setext h1 contains first line");
|
|
2312
|
-
assert_contains(html, "a multi-line", "Multi-line setext h1 contains second line");
|
|
2313
|
-
assert_contains(html, "setext header</h1>", "Multi-line setext h1 contains last line");
|
|
2314
|
-
apex_free_string(html);
|
|
2315
|
-
|
|
2316
|
-
/* Test multi-line setext header (h2) */
|
|
2317
|
-
const char *multiline_h2 = "Another\nheader\nwith\nmultiple\nlines\n--------";
|
|
2318
|
-
html = apex_markdown_to_html(multiline_h2, strlen(multiline_h2), &opts);
|
|
2319
|
-
assert_contains(html, "<h2", "Multi-line setext h2 tag");
|
|
2320
|
-
assert_contains(html, "Another", "Multi-line setext h2 contains first line");
|
|
2321
|
-
assert_contains(html, "multiple", "Multi-line setext h2 contains middle line");
|
|
2322
|
-
assert_contains(html, "lines</h2>", "Multi-line setext h2 contains last line");
|
|
2323
|
-
apex_free_string(html);
|
|
2324
|
-
|
|
2325
|
-
/* Test link title with double quotes */
|
|
2326
|
-
const char *link_double = "[Link](https://example.com \"Double quote title\")";
|
|
2327
|
-
html = apex_markdown_to_html(link_double, strlen(link_double), &opts);
|
|
2328
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Link with double quote title has href");
|
|
2329
|
-
assert_contains(html, "title=\"Double quote title\"", "Link with double quote title");
|
|
2330
|
-
apex_free_string(html);
|
|
2331
|
-
|
|
2332
|
-
/* Test link title with single quotes */
|
|
2333
|
-
const char *link_single = "[Link](https://example.com 'Single quote title')";
|
|
2334
|
-
html = apex_markdown_to_html(link_single, strlen(link_single), &opts);
|
|
2335
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Link with single quote title has href");
|
|
2336
|
-
assert_contains(html, "title=\"Single quote title\"", "Link with single quote title");
|
|
2337
|
-
apex_free_string(html);
|
|
2338
|
-
|
|
2339
|
-
/* Test link title with parentheses */
|
|
2340
|
-
const char *link_paren = "[Link](https://example.com (Parentheses title))";
|
|
2341
|
-
html = apex_markdown_to_html(link_paren, strlen(link_paren), &opts);
|
|
2342
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Link with parentheses title has href");
|
|
2343
|
-
assert_contains(html, "title=\"Parentheses title\"", "Link with parentheses title");
|
|
2344
|
-
apex_free_string(html);
|
|
2345
|
-
|
|
2346
|
-
/* Test image title with double quotes */
|
|
2347
|
-
const char *img_double = "";
|
|
2348
|
-
html = apex_markdown_to_html(img_double, strlen(img_double), &opts);
|
|
2349
|
-
assert_contains(html, "<img src=\"image.png\"", "Image with double quote title has src");
|
|
2350
|
-
assert_contains(html, "title=\"Double quote title\"", "Image with double quote title");
|
|
2351
|
-
apex_free_string(html);
|
|
2352
|
-
|
|
2353
|
-
/* Test image title with single quotes */
|
|
2354
|
-
const char *img_single = "";
|
|
2355
|
-
html = apex_markdown_to_html(img_single, strlen(img_single), &opts);
|
|
2356
|
-
assert_contains(html, "<img src=\"image.png\"", "Image with single quote title has src");
|
|
2357
|
-
assert_contains(html, "title=\"Single quote title\"", "Image with single quote title");
|
|
2358
|
-
apex_free_string(html);
|
|
2359
|
-
|
|
2360
|
-
/* Test image title with parentheses */
|
|
2361
|
-
const char *img_paren = ")";
|
|
2362
|
-
html = apex_markdown_to_html(img_paren, strlen(img_paren), &opts);
|
|
2363
|
-
assert_contains(html, "<img src=\"image.png\"", "Image with parentheses title has src");
|
|
2364
|
-
assert_contains(html, "title=\"Parentheses title\"", "Image with parentheses title");
|
|
2365
|
-
apex_free_string(html);
|
|
2366
|
-
|
|
2367
|
-
/* Test reference link with double quote title */
|
|
2368
|
-
const char *ref_double = "[Ref][id]\n\n[id]: https://example.com \"Reference title\"";
|
|
2369
|
-
html = apex_markdown_to_html(ref_double, strlen(ref_double), &opts);
|
|
2370
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Reference link with double quote title has href");
|
|
2371
|
-
assert_contains(html, "title=\"Reference title\"", "Reference link with double quote title");
|
|
2372
|
-
apex_free_string(html);
|
|
2373
|
-
|
|
2374
|
-
/* Test reference link with single quote title */
|
|
2375
|
-
const char *ref_single = "[Ref][id]\n\n[id]: https://example.com 'Reference title'";
|
|
2376
|
-
html = apex_markdown_to_html(ref_single, strlen(ref_single), &opts);
|
|
2377
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Reference link with single quote title has href");
|
|
2378
|
-
assert_contains(html, "title=\"Reference title\"", "Reference link with single quote title");
|
|
2379
|
-
apex_free_string(html);
|
|
2380
|
-
|
|
2381
|
-
/* Test reference link with parentheses title */
|
|
2382
|
-
const char *ref_paren = "[Ref][id]\n\n[id]: https://example.com (Reference title)";
|
|
2383
|
-
html = apex_markdown_to_html(ref_paren, strlen(ref_paren), &opts);
|
|
2384
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Reference link with parentheses title has href");
|
|
2385
|
-
assert_contains(html, "title=\"Reference title\"", "Reference link with parentheses title");
|
|
2386
|
-
apex_free_string(html);
|
|
2387
|
-
|
|
2388
|
-
/* Test in unified mode as well */
|
|
2389
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
2390
|
-
const char *unified_test = "Multi\nLine\nHeader\n========\n\n[Link](url 'Title')";
|
|
2391
|
-
html = apex_markdown_to_html(unified_test, strlen(unified_test), &unified_opts);
|
|
2392
|
-
assert_contains(html, "<h1", "Multi-line setext header works in unified mode");
|
|
2393
|
-
assert_contains(html, "Multi\nLine\nHeader</h1>", "Multi-line setext header content in unified mode");
|
|
2394
|
-
assert_contains(html, "title=\"Title\"", "Link title with single quotes works in unified mode");
|
|
2395
|
-
apex_free_string(html);
|
|
2396
|
-
|
|
2397
|
-
bool had_failures = suite_end(suite_failures);
|
|
2398
|
-
print_suite_title("MMD 6 Features Tests", had_failures, false);
|
|
2399
|
-
}
|
|
2400
|
-
|
|
2401
|
-
/**
|
|
2402
|
-
* Test emoji support
|
|
2403
|
-
*/
|
|
2404
|
-
|
|
2405
|
-
void test_emoji(void) {
|
|
2406
|
-
int suite_failures = suite_start();
|
|
2407
|
-
print_suite_title("Emoji Tests", false, true);
|
|
2408
|
-
|
|
2409
|
-
apex_options opts = apex_options_default();
|
|
2410
|
-
opts.enable_marked_extensions = true;
|
|
2411
|
-
char *html;
|
|
2412
|
-
|
|
2413
|
-
/* Test basic emoji */
|
|
2414
|
-
html = apex_markdown_to_html("Hello :smile: world", 19, &opts);
|
|
2415
|
-
assert_contains(html, "😄", "Smile emoji converted");
|
|
2416
|
-
apex_free_string(html);
|
|
2417
|
-
|
|
2418
|
-
/* Test multiple emoji */
|
|
2419
|
-
html = apex_markdown_to_html(":thumbsup: :heart: :rocket:", 27, &opts);
|
|
2420
|
-
assert_contains(html, "👍", "Thumbs up emoji");
|
|
2421
|
-
assert_contains(html, "❤", "Heart emoji");
|
|
2422
|
-
assert_contains(html, "🚀", "Rocket emoji");
|
|
2423
|
-
apex_free_string(html);
|
|
2424
|
-
|
|
2425
|
-
/* Test emoji in text */
|
|
2426
|
-
html = apex_markdown_to_html("I :heart: coding!", 17, &opts);
|
|
2427
|
-
assert_contains(html, "❤", "Emoji in sentence");
|
|
2428
|
-
assert_contains(html, "coding", "Regular text preserved");
|
|
2429
|
-
apex_free_string(html);
|
|
2430
|
-
|
|
2431
|
-
/* Test unknown emoji (should be preserved) */
|
|
2432
|
-
html = apex_markdown_to_html(":notarealemojicode:", 19, &opts);
|
|
2433
|
-
assert_contains(html, ":notarealemojicode:", "Unknown emoji preserved");
|
|
2434
|
-
apex_free_string(html);
|
|
2435
|
-
|
|
2436
|
-
/* Test emoji variations */
|
|
2437
|
-
html = apex_markdown_to_html(":star: :warning: :+1:", 21, &opts);
|
|
2438
|
-
assert_contains(html, "⭐", "Star emoji");
|
|
2439
|
-
assert_contains(html, "⚠", "Warning emoji");
|
|
2440
|
-
assert_contains(html, "👍", "Plus one emoji");
|
|
2441
|
-
apex_free_string(html);
|
|
2442
|
-
|
|
2443
|
-
/* Emoji NOT converted inside inline code span */
|
|
2444
|
-
html = apex_markdown_to_html("Use `:smile:` for emoji syntax", 30, &opts);
|
|
2445
|
-
assert_contains(html, "<code>:smile:</code>", "Emoji preserved as literal in inline code");
|
|
2446
|
-
assert_not_contains(html, "😄", "Emoji character not in code span output");
|
|
2447
|
-
apex_free_string(html);
|
|
2448
|
-
|
|
2449
|
-
/* Emoji NOT converted inside fenced code block */
|
|
2450
|
-
const char *fenced_emoji = "```\n:smile:\n:rocket:\n```";
|
|
2451
|
-
html = apex_markdown_to_html(fenced_emoji, strlen(fenced_emoji), &opts);
|
|
2452
|
-
assert_contains(html, ":smile:", "Emoji pattern preserved in fenced block");
|
|
2453
|
-
assert_contains(html, ":rocket:", "Second emoji preserved in fenced block");
|
|
2454
|
-
apex_free_string(html);
|
|
2455
|
-
|
|
2456
|
-
/* Emoji NOT converted inside indented code block */
|
|
2457
|
-
const char *indented_emoji = " :smile:\n :rocket:";
|
|
2458
|
-
html = apex_markdown_to_html(indented_emoji, strlen(indented_emoji), &opts);
|
|
2459
|
-
assert_contains(html, ":smile:", "Emoji pattern preserved in indented block");
|
|
2460
|
-
assert_contains(html, ":rocket:", "Second emoji preserved in indented block");
|
|
2461
|
-
apex_free_string(html);
|
|
2462
|
-
|
|
2463
|
-
bool had_failures = suite_end(suite_failures);
|
|
2464
|
-
print_suite_title("Emoji Tests", had_failures, false);
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
/**
|
|
2468
|
-
* Test special markers (page breaks, pauses, end-of-block)
|
|
2469
|
-
*/
|
|
2470
|
-
|
|
2471
|
-
void test_special_markers(void) {
|
|
2472
|
-
int suite_failures = suite_start();
|
|
2473
|
-
print_suite_title("Special Markers Tests", false, true);
|
|
2474
|
-
|
|
2475
|
-
apex_options opts = apex_options_default();
|
|
2476
|
-
opts.enable_marked_extensions = true;
|
|
2477
|
-
char *html;
|
|
2478
|
-
|
|
2479
|
-
/* Test page break HTML comment */
|
|
2480
|
-
html = apex_markdown_to_html("Before\n\n<!--BREAK-->\n\nAfter", 28, &opts);
|
|
2481
|
-
assert_contains(html, "page-break-after", "Page break marker");
|
|
2482
|
-
assert_contains(html, "Before", "Content before break");
|
|
2483
|
-
assert_contains(html, "After", "Content after break");
|
|
2484
|
-
apex_free_string(html);
|
|
2485
|
-
|
|
2486
|
-
/* Test Kramdown page break */
|
|
2487
|
-
html = apex_markdown_to_html("Page 1\n\n{::pagebreak /}\n\nPage 2", 32, &opts);
|
|
2488
|
-
assert_contains(html, "page-break-after", "Kramdown page break");
|
|
2489
|
-
assert_contains(html, "Page 2", "Content after pagebreak");
|
|
2490
|
-
apex_free_string(html);
|
|
2491
|
-
|
|
2492
|
-
/* Test autoscroll pause */
|
|
2493
|
-
html = apex_markdown_to_html("Text\n\n<!--PAUSE:5-->\n\nMore text", 31, &opts);
|
|
2494
|
-
assert_contains(html, "data-pause", "Pause marker");
|
|
2495
|
-
assert_contains(html, "data-pause=\"5\"", "Pause duration");
|
|
2496
|
-
assert_contains(html, "More text", "Content after pause");
|
|
2497
|
-
apex_free_string(html);
|
|
2498
|
-
|
|
2499
|
-
/* Test end-of-block marker */
|
|
2500
|
-
const char *eob = "- Item 1\n\n^\n\n- Item 2";
|
|
2501
|
-
html = apex_markdown_to_html(eob, strlen(eob), &opts);
|
|
2502
|
-
// End of block should separate lists
|
|
2503
|
-
assert_contains(html, "<ul>", "Lists created");
|
|
2504
|
-
apex_free_string(html);
|
|
2505
|
-
|
|
2506
|
-
/* Test empty HTML comment as block separator (CommonMark spec) */
|
|
2507
|
-
const char *html_comment_separator = "- foo\n- bar\n\n<!-- -->\n\n- baz\n- bim";
|
|
2508
|
-
html = apex_markdown_to_html(html_comment_separator, strlen(html_comment_separator), &opts);
|
|
2509
|
-
// Should create two separate lists, not one merged list
|
|
2510
|
-
const char *first_ul = strstr(html, "<ul>");
|
|
2511
|
-
const char *second_ul = first_ul ? strstr(first_ul + 1, "<ul>") : NULL;
|
|
2512
|
-
if (second_ul != NULL) {
|
|
2513
|
-
test_result(true, "Empty HTML comment separates lists");
|
|
2514
|
-
} else {
|
|
2515
|
-
test_result(false, "Empty HTML comment does not separate lists");
|
|
2516
|
-
}
|
|
2517
|
-
assert_contains(html, "<li>foo</li>", "First list contains foo");
|
|
2518
|
-
assert_contains(html, "<li>bar</li>", "First list contains bar");
|
|
2519
|
-
assert_contains(html, "<li>baz</li>", "Second list contains baz");
|
|
2520
|
-
assert_contains(html, "<li>bim</li>", "Second list contains bim");
|
|
2521
|
-
assert_contains(html, "<!-- -->", "Empty HTML comment preserved");
|
|
2522
|
-
apex_free_string(html);
|
|
2523
|
-
|
|
2524
|
-
/* Test multiple page breaks */
|
|
2525
|
-
const char *multi = "Section 1\n\n<!--BREAK-->\n\nSection 2\n\n<!--BREAK-->\n\nSection 3";
|
|
2526
|
-
html = apex_markdown_to_html(multi, strlen(multi), &opts);
|
|
2527
|
-
assert_contains(html, "page-break-after", "Multiple page breaks");
|
|
2528
|
-
assert_contains(html, "Section 1", "First section");
|
|
2529
|
-
assert_contains(html, "Section 3", "Last section");
|
|
2530
|
-
apex_free_string(html);
|
|
2531
|
-
|
|
2532
|
-
/* Special markers NOT converted inside inline code span */
|
|
2533
|
-
html = apex_markdown_to_html("`<!--PAUSE:15-->`", 17, &opts);
|
|
2534
|
-
assert_contains(html, "<code><!--PAUSE:15--></code>", "Pause marker preserved in inline code");
|
|
2535
|
-
assert_not_contains(html, "data-pause", "Pause marker not rendered in inline code");
|
|
2536
|
-
apex_free_string(html);
|
|
2537
|
-
|
|
2538
|
-
/* Special markers NOT converted inside indented code block */
|
|
2539
|
-
const char *indented_break = " <!--BREAK-->";
|
|
2540
|
-
html = apex_markdown_to_html(indented_break, strlen(indented_break), &opts);
|
|
2541
|
-
assert_contains(html, "<!--BREAK-->", "Break marker preserved in indented code");
|
|
2542
|
-
assert_not_contains(html, "page-break-after", "Break marker not rendered in indented code");
|
|
2543
|
-
apex_free_string(html);
|
|
2544
|
-
|
|
2545
|
-
/* Special markers NOT converted inside fenced code block */
|
|
2546
|
-
const char *fenced = "```\n<!--BREAK-->\n```";
|
|
2547
|
-
html = apex_markdown_to_html(fenced, strlen(fenced), &opts);
|
|
2548
|
-
assert_contains(html, "<!--BREAK-->", "Break marker preserved in fenced code");
|
|
2549
|
-
assert_not_contains(html, "page-break-after", "Break marker not rendered in fenced code");
|
|
2550
|
-
apex_free_string(html);
|
|
2551
|
-
|
|
2552
|
-
bool had_failures = suite_end(suite_failures);
|
|
2553
|
-
print_suite_title("Special Markers Tests", had_failures, false);
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
/**
|
|
2557
|
-
* Test inline tables from CSV/TSV
|
|
2558
|
-
*/
|
|
2559
|
-
|
|
2560
|
-
void test_advanced_footnotes(void) {
|
|
2561
|
-
int suite_failures = suite_start();
|
|
2562
|
-
print_suite_title("Advanced Footnotes Tests", false, true);
|
|
2563
|
-
|
|
2564
|
-
apex_options opts = apex_options_for_mode(APEX_MODE_KRAMDOWN);
|
|
2565
|
-
char *html;
|
|
2566
|
-
|
|
2567
|
-
/* Direct call: cover NULL-root early return */
|
|
2568
|
-
test_result(apex_process_advanced_footnotes(NULL, NULL) == NULL, "advanced footnotes: NULL root returns NULL");
|
|
2569
|
-
|
|
2570
|
-
/* Test basic footnote */
|
|
2571
|
-
const char *basic = "Text[^1]\n\n[^1]: Footnote text";
|
|
2572
|
-
html = apex_markdown_to_html(basic, strlen(basic), &opts);
|
|
2573
|
-
assert_contains(html, "footnote", "Footnote generated");
|
|
2574
|
-
apex_free_string(html);
|
|
2575
|
-
|
|
2576
|
-
/* Test Kramdown inline footnote: ^[text] */
|
|
2577
|
-
const char *kramdown_inline = "Text^[Kramdown inline footnote]";
|
|
2578
|
-
html = apex_markdown_to_html(kramdown_inline, strlen(kramdown_inline), &opts);
|
|
2579
|
-
assert_contains(html, "footnote", "Kramdown inline footnote");
|
|
2580
|
-
assert_contains(html, "Kramdown inline footnote", "Kramdown footnote content");
|
|
2581
|
-
apex_free_string(html);
|
|
2582
|
-
|
|
2583
|
-
/* Test MMD inline footnote: [^text with spaces] */
|
|
2584
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
2585
|
-
const char *mmd_inline = "Text[^MMD inline footnote with spaces]";
|
|
2586
|
-
html = apex_markdown_to_html(mmd_inline, strlen(mmd_inline), &mmd_opts);
|
|
2587
|
-
assert_contains(html, "footnote", "MMD inline footnote");
|
|
2588
|
-
assert_contains(html, "MMD inline footnote with spaces", "MMD footnote content");
|
|
2589
|
-
apex_free_string(html);
|
|
2590
|
-
|
|
2591
|
-
/* Test regular reference (no spaces) still works */
|
|
2592
|
-
const char *reference = "Text[^ref]\n\n[^ref]: Definition";
|
|
2593
|
-
html = apex_markdown_to_html(reference, strlen(reference), &mmd_opts);
|
|
2594
|
-
assert_contains(html, "footnote", "Regular reference footnote");
|
|
2595
|
-
assert_contains(html, "Definition", "Reference footnote content");
|
|
2596
|
-
apex_free_string(html);
|
|
2597
|
-
|
|
2598
|
-
/* Test multiple inline footnotes */
|
|
2599
|
-
const char *multiple = "First^[one] and second^[two] footnotes";
|
|
2600
|
-
html = apex_markdown_to_html(multiple, strlen(multiple), &opts);
|
|
2601
|
-
assert_contains(html, "one", "First inline footnote");
|
|
2602
|
-
assert_contains(html, "two", "Second inline footnote");
|
|
2603
|
-
apex_free_string(html);
|
|
2604
|
-
|
|
2605
|
-
/* Test inline footnote with formatting */
|
|
2606
|
-
const char *formatted = "Text^[footnote with **bold**]";
|
|
2607
|
-
html = apex_markdown_to_html(formatted, strlen(formatted), &opts);
|
|
2608
|
-
assert_contains(html, "footnote", "Formatted inline footnote");
|
|
2609
|
-
/* Note: Markdown in inline footnotes handled by cmark-gfm */
|
|
2610
|
-
apex_free_string(html);
|
|
2611
|
-
|
|
2612
|
-
/* Test advanced footnote with multiple paragraphs, list, and fenced code (```).
|
|
2613
|
-
* This should exercise the reparse path for block-level content inside footnotes.
|
|
2614
|
-
*/
|
|
2615
|
-
const char *blocky =
|
|
2616
|
-
"Text[^a]\n"
|
|
2617
|
-
"\n"
|
|
2618
|
-
"[^a]: First para\n"
|
|
2619
|
-
"\n"
|
|
2620
|
-
" Second para\n"
|
|
2621
|
-
"\n"
|
|
2622
|
-
" - item1\n"
|
|
2623
|
-
" - item2\n"
|
|
2624
|
-
"\n"
|
|
2625
|
-
" ```\n"
|
|
2626
|
-
" code\n"
|
|
2627
|
-
" ```\n";
|
|
2628
|
-
html = apex_markdown_to_html(blocky, strlen(blocky), &opts);
|
|
2629
|
-
assert_contains(html, "<p>First para</p>", "Advanced footnote: first paragraph");
|
|
2630
|
-
assert_contains(html, "<p>Second para</p>", "Advanced footnote: second paragraph");
|
|
2631
|
-
assert_contains(html, "<ul>", "Advanced footnote: list parsed");
|
|
2632
|
-
assert_contains(html, "<li>item1</li>", "Advanced footnote: list item 1");
|
|
2633
|
-
assert_contains(html, "<pre><code>code", "Advanced footnote: fenced code block parsed");
|
|
2634
|
-
apex_free_string(html);
|
|
2635
|
-
|
|
2636
|
-
/* Test advanced footnote with indented code block (4+ spaces after newline). */
|
|
2637
|
-
const char *indented_code =
|
|
2638
|
-
"Text[^b]\n"
|
|
2639
|
-
"\n"
|
|
2640
|
-
"[^b]: Intro\n"
|
|
2641
|
-
"\n"
|
|
2642
|
-
" indented\n"
|
|
2643
|
-
" code\n";
|
|
2644
|
-
html = apex_markdown_to_html(indented_code, strlen(indented_code), &opts);
|
|
2645
|
-
assert_contains(html, "<p>Intro</p>", "Indented code footnote: intro paragraph");
|
|
2646
|
-
assert_contains(html, "<pre><code>indented", "Indented code footnote: code block parsed");
|
|
2647
|
-
apex_free_string(html);
|
|
2648
|
-
|
|
2649
|
-
/* Test advanced footnote with fenced code using ~~~ (alternate fence detection). */
|
|
2650
|
-
const char *tilde_fence =
|
|
2651
|
-
"Text[^c]\n"
|
|
2652
|
-
"\n"
|
|
2653
|
-
"[^c]: Para\n"
|
|
2654
|
-
"\n"
|
|
2655
|
-
" ~~~\n"
|
|
2656
|
-
" tilde\n"
|
|
2657
|
-
" ~~~\n";
|
|
2658
|
-
html = apex_markdown_to_html(tilde_fence, strlen(tilde_fence), &opts);
|
|
2659
|
-
assert_contains(html, "<pre><code>tilde", "Tilde fence footnote: code block parsed");
|
|
2660
|
-
apex_free_string(html);
|
|
2661
|
-
|
|
2662
|
-
/* Test ordered list inside footnote. */
|
|
2663
|
-
const char *ordered_list =
|
|
2664
|
-
"Text[^d]\n"
|
|
2665
|
-
"\n"
|
|
2666
|
-
"[^d]: Steps\n"
|
|
2667
|
-
"\n"
|
|
2668
|
-
" 1. one\n"
|
|
2669
|
-
" 2. two\n";
|
|
2670
|
-
html = apex_markdown_to_html(ordered_list, strlen(ordered_list), &opts);
|
|
2671
|
-
assert_contains(html, "<p>Steps</p>", "Ordered list footnote: intro paragraph");
|
|
2672
|
-
assert_contains(html, "<ol>", "Ordered list footnote: ordered list parsed");
|
|
2673
|
-
assert_contains(html, "<li>one</li>", "Ordered list footnote: first item");
|
|
2674
|
-
assert_contains(html, "<li>two</li>", "Ordered list footnote: second item");
|
|
2675
|
-
apex_free_string(html);
|
|
2676
|
-
|
|
2677
|
-
bool had_failures = suite_end(suite_failures);
|
|
2678
|
-
print_suite_title("Advanced Footnotes Tests", had_failures, false);
|
|
2679
|
-
}
|
|
2680
|
-
|
|
2681
|
-
/**
|
|
2682
|
-
* Test standalone document output
|
|
2683
|
-
*/
|
|
2684
|
-
|
|
2685
|
-
void test_sup_sub(void) {
|
|
2686
|
-
int suite_failures = suite_start();
|
|
2687
|
-
print_suite_title("Superscript, Subscript, Underline, Delete, and Highlight Tests", false, true);
|
|
2688
|
-
|
|
2689
|
-
apex_options opts = apex_options_default();
|
|
2690
|
-
opts.enable_sup_sub = true;
|
|
2691
|
-
char *html;
|
|
2692
|
-
|
|
2693
|
-
/* ===== SUBSCRIPT TESTS ===== */
|
|
2694
|
-
|
|
2695
|
-
/* Test H~2~O for subscript 2 (paired tildes within word) */
|
|
2696
|
-
html = apex_markdown_to_html("H~2~O", 5, &opts);
|
|
2697
|
-
assert_contains(html, "<sub>2</sub>", "H~2~O creates subscript 2");
|
|
2698
|
-
assert_contains(html, "H<sub>2</sub>O", "Subscript within word");
|
|
2699
|
-
if (strstr(html, "<u>2</u>") == NULL) {
|
|
2700
|
-
test_result(true, "H~2~O is subscript, not underline");
|
|
2701
|
-
} else {
|
|
2702
|
-
test_result(false, "H~2~O incorrectly treated as underline");
|
|
2703
|
-
}
|
|
2704
|
-
apex_free_string(html);
|
|
2705
|
-
|
|
2706
|
-
/* Test H~2~SO~4~ for both 2 and 4 as subscripts */
|
|
2707
|
-
html = apex_markdown_to_html("H~2~SO~4~", 9, &opts);
|
|
2708
|
-
assert_contains(html, "<sub>2</sub>", "H~2~SO~4~ creates subscript 2");
|
|
2709
|
-
assert_contains(html, "<sub>4</sub>", "H~2~SO~4~ creates subscript 4");
|
|
2710
|
-
assert_contains(html, "H<sub>2</sub>SO<sub>4</sub>", "Multiple subscripts within word");
|
|
2711
|
-
apex_free_string(html);
|
|
2712
|
-
|
|
2713
|
-
/* Test subscript ends at sentence terminators */
|
|
2714
|
-
html = apex_markdown_to_html("H~2.O", 5, &opts);
|
|
2715
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at period");
|
|
2716
|
-
apex_free_string(html);
|
|
2717
|
-
|
|
2718
|
-
html = apex_markdown_to_html("H~2,O", 5, &opts);
|
|
2719
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at comma");
|
|
2720
|
-
apex_free_string(html);
|
|
2721
|
-
|
|
2722
|
-
html = apex_markdown_to_html("H~2;O", 5, &opts);
|
|
2723
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at semicolon");
|
|
2724
|
-
apex_free_string(html);
|
|
2725
|
-
|
|
2726
|
-
html = apex_markdown_to_html("H~2:O", 5, &opts);
|
|
2727
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at colon");
|
|
2728
|
-
apex_free_string(html);
|
|
2729
|
-
|
|
2730
|
-
html = apex_markdown_to_html("H~2!O", 5, &opts);
|
|
2731
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at exclamation");
|
|
2732
|
-
apex_free_string(html);
|
|
2733
|
-
|
|
2734
|
-
html = apex_markdown_to_html("H~2?O", 5, &opts);
|
|
2735
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at question mark");
|
|
2736
|
-
apex_free_string(html);
|
|
2737
|
-
|
|
2738
|
-
/* Test subscript ends at space */
|
|
2739
|
-
html = apex_markdown_to_html("H~2 O", 5, &opts);
|
|
2740
|
-
assert_contains(html, "<sub>2</sub>", "Subscript stops at space");
|
|
2741
|
-
assert_contains(html, "H<sub>2</sub> O", "Space after subscript");
|
|
2742
|
-
apex_free_string(html);
|
|
2743
|
-
|
|
2744
|
-
/* ===== SUPERSCRIPT TESTS ===== */
|
|
2745
|
-
|
|
2746
|
-
/* Test basic superscript */
|
|
2747
|
-
html = apex_markdown_to_html("m^2", 3, &opts);
|
|
2748
|
-
assert_contains(html, "<sup>2</sup>", "Basic superscript m^2");
|
|
2749
|
-
assert_contains(html, "m<sup>2</sup>", "Superscript in context");
|
|
2750
|
-
apex_free_string(html);
|
|
2751
|
-
|
|
2752
|
-
/* Test superscript ends at space */
|
|
2753
|
-
html = apex_markdown_to_html("x^2 + y^2", 9, &opts);
|
|
2754
|
-
assert_contains(html, "<sup>2</sup>", "Superscript stops at space");
|
|
2755
|
-
assert_contains(html, "x<sup>2</sup>", "First superscript");
|
|
2756
|
-
assert_contains(html, "y<sup>2</sup>", "Second superscript");
|
|
2757
|
-
apex_free_string(html);
|
|
2758
|
-
|
|
2759
|
-
/* Test superscript ends at sentence terminators */
|
|
2760
|
-
html = apex_markdown_to_html("x^2.", 4, &opts);
|
|
2761
|
-
assert_contains(html, "<sup>2</sup>", "Superscript stops at period");
|
|
2762
|
-
apex_free_string(html);
|
|
2763
|
-
|
|
2764
|
-
html = apex_markdown_to_html("x^2,", 4, &opts);
|
|
2765
|
-
assert_contains(html, "<sup>2</sup>", "Superscript stops at comma");
|
|
2766
|
-
apex_free_string(html);
|
|
2767
|
-
|
|
2768
|
-
html = apex_markdown_to_html("E = mc^2!", 9, &opts);
|
|
2769
|
-
assert_contains(html, "<sup>2</sup>", "Superscript stops at exclamation");
|
|
2770
|
-
apex_free_string(html);
|
|
2771
|
-
|
|
2772
|
-
/* Test multiple superscripts */
|
|
2773
|
-
html = apex_markdown_to_html("x^2 + y^2 = z^2", 15, &opts);
|
|
2774
|
-
assert_contains(html, "x<sup>2</sup>", "First superscript");
|
|
2775
|
-
assert_contains(html, "y<sup>2</sup>", "Second superscript");
|
|
2776
|
-
assert_contains(html, "z<sup>2</sup>", "Third superscript");
|
|
2777
|
-
apex_free_string(html);
|
|
2778
|
-
|
|
2779
|
-
/* ===== UNDERLINE TESTS ===== */
|
|
2780
|
-
|
|
2781
|
-
/* Test underline with tildes at word boundaries */
|
|
2782
|
-
html = apex_markdown_to_html("text ~underline~ text", 22, &opts);
|
|
2783
|
-
assert_contains(html, "<u>underline</u>", "Tildes at word boundaries create underline");
|
|
2784
|
-
assert_contains(html, "text <u>underline</u> text", "Underline in context");
|
|
2785
|
-
if (strstr(html, "<sub>underline</sub>") == NULL) {
|
|
2786
|
-
test_result(true, "~underline~ is underline, not subscript");
|
|
2787
|
-
} else {
|
|
2788
|
-
test_result(false, "~underline~ incorrectly treated as subscript");
|
|
2789
|
-
}
|
|
2790
|
-
apex_free_string(html);
|
|
2791
|
-
|
|
2792
|
-
/* Test underline with single word */
|
|
2793
|
-
html = apex_markdown_to_html("~h2o~", 6, &opts);
|
|
2794
|
-
assert_contains(html, "<u>h2o</u>", "~h2o~ creates underline");
|
|
2795
|
-
if (strstr(html, "<sub>") == NULL) {
|
|
2796
|
-
test_result(true, "~h2o~ is underline, not subscript");
|
|
2797
|
-
} else {
|
|
2798
|
-
test_result(false, "~h2o~ incorrectly treated as subscript");
|
|
2799
|
-
}
|
|
2800
|
-
apex_free_string(html);
|
|
2801
|
-
|
|
2802
|
-
/* ===== STRIKETHROUGH/DELETE TESTS ===== */
|
|
2803
|
-
|
|
2804
|
-
/* Test strikethrough with double tildes */
|
|
2805
|
-
html = apex_markdown_to_html("text ~~deleted text~~ text", 26, &opts);
|
|
2806
|
-
assert_contains(html, "<del>deleted text</del>", "Double tildes create strikethrough");
|
|
2807
|
-
assert_contains(html, "text <del>deleted text</del> text", "Strikethrough in context");
|
|
2808
|
-
apex_free_string(html);
|
|
2809
|
-
|
|
2810
|
-
/* Test strikethrough doesn't interfere with subscript */
|
|
2811
|
-
html = apex_markdown_to_html("H~2~O and ~~deleted~~", 21, &opts);
|
|
2812
|
-
assert_contains(html, "<sub>2</sub>", "Subscript still works with strikethrough");
|
|
2813
|
-
assert_contains(html, "<del>deleted</del>", "Strikethrough still works with subscript");
|
|
2814
|
-
apex_free_string(html);
|
|
2815
|
-
|
|
2816
|
-
/* Test strikethrough doesn't interfere with underline */
|
|
2817
|
-
html = apex_markdown_to_html("~underline~ and ~~deleted~~", 27, &opts);
|
|
2818
|
-
assert_contains(html, "<u>underline</u>", "Underline still works with strikethrough");
|
|
2819
|
-
assert_contains(html, "<del>deleted</del>", "Strikethrough still works with underline");
|
|
2820
|
-
apex_free_string(html);
|
|
2821
|
-
|
|
2822
|
-
/* ===== HIGHLIGHT TESTS ===== */
|
|
2823
|
-
|
|
2824
|
-
/* Test highlight with double equals */
|
|
2825
|
-
html = apex_markdown_to_html("text ==highlighted text== text", 30, &opts);
|
|
2826
|
-
assert_contains(html, "<mark>highlighted text</mark>", "Double equals create highlight");
|
|
2827
|
-
assert_contains(html, "text <mark>highlighted text</mark> text", "Highlight in context");
|
|
2828
|
-
apex_free_string(html);
|
|
2829
|
-
|
|
2830
|
-
/* Test highlight with single word */
|
|
2831
|
-
html = apex_markdown_to_html("==highlight==", 14, &opts);
|
|
2832
|
-
assert_contains(html, "<mark>highlight</mark>", "Single word highlight");
|
|
2833
|
-
apex_free_string(html);
|
|
2834
|
-
|
|
2835
|
-
/* Test highlight with multiple words */
|
|
2836
|
-
html = apex_markdown_to_html("==this is highlighted==", 24, &opts);
|
|
2837
|
-
assert_contains(html, "<mark>this is highlighted</mark>", "Multi-word highlight");
|
|
2838
|
-
apex_free_string(html);
|
|
2839
|
-
|
|
2840
|
-
/* Test highlight doesn't break Setext h1 */
|
|
2841
|
-
html = apex_markdown_to_html("Header\n==\n\n==highlight==", 25, &opts);
|
|
2842
|
-
assert_contains(html, "<h1", "Setext h1 still works");
|
|
2843
|
-
assert_contains(html, "Header</h1>", "Setext h1 content");
|
|
2844
|
-
assert_contains(html, "<mark>highlight</mark>", "Highlight after Setext h1");
|
|
2845
|
-
/* Verify the == after header is not treated as highlight */
|
|
2846
|
-
if (strstr(html, "<mark></mark>") == NULL || strstr(html, "<mark>\n</mark>") == NULL) {
|
|
2847
|
-
test_result(true, "== after Setext h1 doesn't break header");
|
|
2848
|
-
} else {
|
|
2849
|
-
test_result(false, "== after Setext h1 breaks header");
|
|
2850
|
-
}
|
|
2851
|
-
apex_free_string(html);
|
|
2852
|
-
|
|
2853
|
-
/* Test highlight with Setext h2 (===) */
|
|
2854
|
-
html = apex_markdown_to_html("Header\n---\n\n==highlight==", 25, &opts);
|
|
2855
|
-
assert_contains(html, "<h2", "Setext h2 still works");
|
|
2856
|
-
assert_contains(html, "Header</h2>", "Setext h2 content");
|
|
2857
|
-
assert_contains(html, "<mark>highlight</mark>", "Highlight after Setext h2");
|
|
2858
|
-
apex_free_string(html);
|
|
2859
|
-
|
|
2860
|
-
/* Test highlight in various contexts */
|
|
2861
|
-
html = apex_markdown_to_html("Before ==highlight== after", 26, &opts);
|
|
2862
|
-
assert_contains(html, "<mark>highlight</mark>", "Highlight in paragraph");
|
|
2863
|
-
apex_free_string(html);
|
|
2864
|
-
|
|
2865
|
-
html = apex_markdown_to_html("**bold ==highlight== bold**", 27, &opts);
|
|
2866
|
-
assert_contains(html, "<mark>highlight</mark>", "Highlight in bold");
|
|
2867
|
-
apex_free_string(html);
|
|
2868
|
-
|
|
2869
|
-
/* ===== INTERACTION TESTS ===== */
|
|
2870
|
-
|
|
2871
|
-
/* Test that sup/sub is disabled when option is off */
|
|
2872
|
-
apex_options no_sup_sub = apex_options_default();
|
|
2873
|
-
no_sup_sub.enable_sup_sub = false;
|
|
2874
|
-
html = apex_markdown_to_html("H^2 O", 5, &no_sup_sub);
|
|
2875
|
-
if (strstr(html, "<sup>") == NULL) {
|
|
2876
|
-
test_result(true, "Sup/sub disabled when option is off");
|
|
2877
|
-
} else {
|
|
2878
|
-
test_result(false, "Sup/sub not disabled when option is off");
|
|
2879
|
-
}
|
|
2880
|
-
apex_free_string(html);
|
|
2881
|
-
|
|
2882
|
-
/* Test that sup/sub is disabled in CommonMark mode */
|
|
2883
|
-
apex_options cm_opts = apex_options_for_mode(APEX_MODE_COMMONMARK);
|
|
2884
|
-
html = apex_markdown_to_html("H^2 O", 5, &cm_opts);
|
|
2885
|
-
if (strstr(html, "<sup>") == NULL) {
|
|
2886
|
-
test_result(true, "Sup/sub disabled in CommonMark mode");
|
|
2887
|
-
} else {
|
|
2888
|
-
test_result(false, "Sup/sub not disabled in CommonMark mode");
|
|
2889
|
-
}
|
|
2890
|
-
apex_free_string(html);
|
|
2891
|
-
|
|
2892
|
-
/* Test that sup/sub is enabled in Unified mode */
|
|
2893
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
2894
|
-
html = apex_markdown_to_html("H^2 O", 5, &unified_opts);
|
|
2895
|
-
assert_contains(html, "<sup>2</sup>", "Sup/sub enabled in Unified mode");
|
|
2896
|
-
apex_free_string(html);
|
|
2897
|
-
|
|
2898
|
-
/* Test that sup/sub is enabled in MultiMarkdown mode */
|
|
2899
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
2900
|
-
html = apex_markdown_to_html("H^2 O", 5, &mmd_opts);
|
|
2901
|
-
assert_contains(html, "<sup>2</sup>", "Sup/sub enabled in MultiMarkdown mode");
|
|
2902
|
-
apex_free_string(html);
|
|
2903
|
-
|
|
2904
|
-
/* Test that ^ and ~ are preserved in math spans */
|
|
2905
|
-
opts.enable_math = true;
|
|
2906
|
-
html = apex_markdown_to_html("Equation: $E=mc^2$", 18, &opts);
|
|
2907
|
-
assert_contains(html, "E=mc^2", "Superscript preserved in math span");
|
|
2908
|
-
if (strstr(html, "<sup>2</sup>") == NULL) {
|
|
2909
|
-
test_result(true, "Superscript not processed inside math span");
|
|
2910
|
-
} else {
|
|
2911
|
-
test_result(false, "Superscript incorrectly processed inside math span");
|
|
2912
|
-
}
|
|
2913
|
-
apex_free_string(html);
|
|
2914
|
-
|
|
2915
|
-
/* Test that ^ is preserved in footnote references */
|
|
2916
|
-
html = apex_markdown_to_html("Text[^ref]", 10, &opts);
|
|
2917
|
-
if (strstr(html, "<sup>ref</sup>") == NULL) {
|
|
2918
|
-
test_result(true, "Superscript not processed in footnote reference");
|
|
2919
|
-
} else {
|
|
2920
|
-
test_result(false, "Superscript incorrectly processed in footnote reference");
|
|
2921
|
-
}
|
|
2922
|
-
apex_free_string(html);
|
|
2923
|
-
|
|
2924
|
-
/* Test that ~ is preserved in critic markup */
|
|
2925
|
-
opts.enable_critic_markup = true;
|
|
2926
|
-
html = apex_markdown_to_html("{~~old~>new~~}", 13, &opts);
|
|
2927
|
-
if (strstr(html, "<sub>old</sub>") == NULL) {
|
|
2928
|
-
test_result(true, "Subscript not processed in critic markup");
|
|
2929
|
-
} else {
|
|
2930
|
-
test_result(false, "Subscript incorrectly processed in critic markup");
|
|
2931
|
-
}
|
|
2932
|
-
apex_free_string(html);
|
|
2933
|
-
opts.enable_critic_markup = false;
|
|
2934
|
-
|
|
2935
|
-
/* ===== CODE BLOCKS: extended syntax not processed ===== */
|
|
2936
|
-
|
|
2937
|
-
/* Indented code block: ^ ~ ~~ == must appear literally, not as sup/sub/underline/strikethrough/highlight */
|
|
2938
|
-
const char *indented_with_extended = "Normal text x^2 here.\n\n code with ~subscript~ and ^caret^\n and ~~strikethrough~~ and ==highlight==\n\nBack to normal.";
|
|
2939
|
-
html = apex_markdown_to_html(indented_with_extended, strlen(indented_with_extended), &opts);
|
|
2940
|
-
assert_contains(html, "<sup>2</sup>", "Superscript processed in normal text");
|
|
2941
|
-
assert_contains(html, "~subscript~", "Subscript not processed in indented code block");
|
|
2942
|
-
assert_contains(html, "^caret^", "Caret not processed as superscript in indented code block");
|
|
2943
|
-
assert_not_contains(html, "<sub>subscript</sub>", "No subscript tag in indented code block");
|
|
2944
|
-
assert_contains(html, "~~strikethrough~~", "Strikethrough not processed in indented code block");
|
|
2945
|
-
assert_contains(html, "==highlight==", "Highlight not processed in indented code block");
|
|
2946
|
-
assert_not_contains(html, "<mark>highlight</mark>", "No mark tag in indented code block");
|
|
2947
|
-
apex_free_string(html);
|
|
2948
|
-
|
|
2949
|
-
/* Fenced code block: same checks */
|
|
2950
|
-
const char *fenced_with_extended = "Normal x^2.\n\n```\ncode with ~subscript~ and ^caret^\nand ~~strikethrough~~ and ==highlight==\n```\n\nBack.";
|
|
2951
|
-
html = apex_markdown_to_html(fenced_with_extended, strlen(fenced_with_extended), &opts);
|
|
2952
|
-
assert_contains(html, "<sup>2</sup>", "Superscript processed in normal text");
|
|
2953
|
-
assert_contains(html, "~subscript~", "Subscript not processed in fenced code block");
|
|
2954
|
-
assert_contains(html, "^caret^", "Caret not processed in fenced code block");
|
|
2955
|
-
assert_not_contains(html, "<sub>subscript</sub>", "No subscript tag in fenced code block");
|
|
2956
|
-
assert_contains(html, "~~strikethrough~~", "Strikethrough not processed in fenced code block");
|
|
2957
|
-
assert_contains(html, "==highlight==", "Highlight not processed in fenced code block");
|
|
2958
|
-
apex_free_string(html);
|
|
2959
|
-
|
|
2960
|
-
/* Inline code: ^ and ~ must not be processed */
|
|
2961
|
-
html = apex_markdown_to_html("Use `x^2` and `H~2~O` in code.", 32, &opts);
|
|
2962
|
-
assert_contains(html, "x^2", "Caret preserved in inline code");
|
|
2963
|
-
assert_contains(html, "H~2~O", "Tildes preserved in inline code");
|
|
2964
|
-
assert_not_contains(html, "<sup>2</sup>", "No superscript in inline code");
|
|
2965
|
-
assert_not_contains(html, "<sub>2</sub>", "No subscript in inline code");
|
|
2966
|
-
apex_free_string(html);
|
|
2967
|
-
|
|
2968
|
-
/* Nested list with 4+ spaces: sup/sub and highlight should be processed (list line, not code block) */
|
|
2969
|
-
const char *list_with_extended = "- Outer item\n - Nested with x^2 and H~2~O\n - And ==highlight== here";
|
|
2970
|
-
html = apex_markdown_to_html(list_with_extended, strlen(list_with_extended), &opts);
|
|
2971
|
-
assert_contains(html, "<sup>2</sup>", "Superscript processed in nested list line");
|
|
2972
|
-
assert_contains(html, "<sub>2</sub>", "Subscript processed in nested list line");
|
|
2973
|
-
assert_contains(html, "<mark>highlight</mark>", "Highlight processed in nested list line");
|
|
2974
|
-
apex_free_string(html);
|
|
2975
|
-
|
|
2976
|
-
/* Indented line that is real code (no list marker): still no processing */
|
|
2977
|
-
const char *real_indented_code = "Paragraph.\n\n actual code ~subscript~ here\n\nBack.";
|
|
2978
|
-
html = apex_markdown_to_html(real_indented_code, strlen(real_indented_code), &opts);
|
|
2979
|
-
assert_contains(html, "~subscript~", "Subscript not processed in real indented code block");
|
|
2980
|
-
assert_not_contains(html, "<sub>subscript</sub>", "No subscript tag in real indented code block");
|
|
2981
|
-
apex_free_string(html);
|
|
2982
|
-
|
|
2983
|
-
bool had_failures = suite_end(suite_failures);
|
|
2984
|
-
print_suite_title("Superscript, Subscript, Underline, Delete, and Highlight Tests", had_failures, false);
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
|
-
/**
|
|
2988
|
-
* Test mixed list markers
|
|
2989
|
-
*/
|
|
2990
|
-
|
|
2991
|
-
void test_mixed_lists(void) {
|
|
2992
|
-
int suite_failures = suite_start();
|
|
2993
|
-
print_suite_title("Mixed List Markers Tests", false, true);
|
|
2994
|
-
|
|
2995
|
-
char *html;
|
|
2996
|
-
|
|
2997
|
-
/* Test mixed markers in unified mode (should merge) */
|
|
2998
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
2999
|
-
const char *mixed_list = "1. First item\n* Second item\n* Third item";
|
|
3000
|
-
html = apex_markdown_to_html(mixed_list, strlen(mixed_list), &unified_opts);
|
|
3001
|
-
assert_contains(html, "<ol>", "Mixed list creates ordered list");
|
|
3002
|
-
assert_contains(html, "<li>First item</li>", "First item in list");
|
|
3003
|
-
assert_contains(html, "<li>Second item</li>", "Second item in list");
|
|
3004
|
-
assert_contains(html, "<li>Third item</li>", "Third item in list");
|
|
3005
|
-
/* Should have only one list, not two */
|
|
3006
|
-
const char *first_ol = strstr(html, "<ol>");
|
|
3007
|
-
const char *second_ol = first_ol ? strstr(first_ol + 1, "<ol>") : NULL;
|
|
3008
|
-
if (second_ol == NULL) {
|
|
3009
|
-
test_result(true, "Mixed markers create single list in unified mode");
|
|
3010
|
-
} else {
|
|
3011
|
-
test_result(false, "Mixed markers create multiple lists in unified mode");
|
|
3012
|
-
}
|
|
3013
|
-
apex_free_string(html);
|
|
3014
|
-
|
|
3015
|
-
/* Test mixed markers in CommonMark mode (should be separate lists) */
|
|
3016
|
-
apex_options cm_opts = apex_options_for_mode(APEX_MODE_COMMONMARK);
|
|
3017
|
-
html = apex_markdown_to_html(mixed_list, strlen(mixed_list), &cm_opts);
|
|
3018
|
-
assert_contains(html, "<ol>", "First list exists");
|
|
3019
|
-
assert_contains(html, "<ul>", "Second list exists");
|
|
3020
|
-
/* Should have two separate lists */
|
|
3021
|
-
first_ol = strstr(html, "<ol>");
|
|
3022
|
-
second_ol = first_ol ? strstr(first_ol + 1, "<ol>") : NULL;
|
|
3023
|
-
const char *first_ul = strstr(html, "<ul>");
|
|
3024
|
-
if (second_ol == NULL && first_ul != NULL) {
|
|
3025
|
-
test_result(true, "Mixed markers create separate lists in CommonMark mode");
|
|
3026
|
-
} else {
|
|
3027
|
-
test_result(false, "Mixed markers not handled correctly in CommonMark mode");
|
|
3028
|
-
}
|
|
3029
|
-
apex_free_string(html);
|
|
3030
|
-
|
|
3031
|
-
/* Test mixed markers with unordered first */
|
|
3032
|
-
const char *mixed_unordered = "* First item\n1. Second item\n2. Third item";
|
|
3033
|
-
html = apex_markdown_to_html(mixed_unordered, strlen(mixed_unordered), &unified_opts);
|
|
3034
|
-
assert_contains(html, "<ul>", "Unordered-first mixed list creates unordered list");
|
|
3035
|
-
assert_contains(html, "<li>First item</li>", "First unordered item");
|
|
3036
|
-
assert_contains(html, "<li>Second item</li>", "Second item inherits unordered");
|
|
3037
|
-
apex_free_string(html);
|
|
3038
|
-
|
|
3039
|
-
/* Test that allow_mixed_list_markers=false creates separate lists even in unified mode */
|
|
3040
|
-
unified_opts.allow_mixed_list_markers = false;
|
|
3041
|
-
html = apex_markdown_to_html(mixed_list, strlen(mixed_list), &unified_opts);
|
|
3042
|
-
first_ol = strstr(html, "<ol>");
|
|
3043
|
-
second_ol = first_ol ? strstr(first_ol + 1, "<ol>") : NULL;
|
|
3044
|
-
first_ul = strstr(html, "<ul>");
|
|
3045
|
-
if (second_ol == NULL && first_ul != NULL) {
|
|
3046
|
-
test_result(true, "--no-mixed-lists disables mixed list merging");
|
|
3047
|
-
} else {
|
|
3048
|
-
test_result(false, "--no-mixed-lists does not disable mixed list merging");
|
|
3049
|
-
}
|
|
3050
|
-
apex_free_string(html);
|
|
3051
|
-
|
|
3052
|
-
/* Regression: alpha lists with nested sublists should stay intact and not leak marker tokens */
|
|
3053
|
-
unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3054
|
-
const char *alpha_with_nested_bullets = "a. Test\nb. Test\n\t- Test\n\t- Test\nc. Test\n";
|
|
3055
|
-
html = apex_markdown_to_html(alpha_with_nested_bullets, strlen(alpha_with_nested_bullets), &unified_opts);
|
|
3056
|
-
assert_contains(html, "<ol style=\"list-style-type: lower-alpha\">", "Alpha list keeps lower-alpha style with nested bullets");
|
|
3057
|
-
assert_contains(html, "<ul>", "Nested bullet list rendered");
|
|
3058
|
-
assert_not_contains(html, "[apex-alpha-list:", "Alpha marker token removed from output");
|
|
3059
|
-
apex_free_string(html);
|
|
3060
|
-
|
|
3061
|
-
const char *alpha_with_nested_ordered = "a. Test\nb. Test\n\t1. Nested one\n\t2. Nested two\nc. Test\n";
|
|
3062
|
-
html = apex_markdown_to_html(alpha_with_nested_ordered, strlen(alpha_with_nested_ordered), &unified_opts);
|
|
3063
|
-
assert_contains(html, "<ol style=\"list-style-type: lower-alpha\">", "Alpha list keeps style with nested ordered list");
|
|
3064
|
-
assert_not_contains(html, "[apex-alpha-list:", "No leaked alpha marker token after nested ordered list");
|
|
3065
|
-
apex_free_string(html);
|
|
3066
|
-
|
|
3067
|
-
const char *alpha_with_nested_alpha = "a. Test\nb. Test\n\tc. Test\n\td. Test\ne. Test\n";
|
|
3068
|
-
html = apex_markdown_to_html(alpha_with_nested_alpha, strlen(alpha_with_nested_alpha), &unified_opts);
|
|
3069
|
-
assert_contains(html, "<ol style=\"list-style-type: lower-alpha\">", "Alpha list keeps style with nested alpha sublist");
|
|
3070
|
-
assert_not_contains(html, "[apex-alpha-list:", "No leaked alpha marker token after nested alpha sublist");
|
|
3071
|
-
assert_not_contains(html, "<p>Test</p>\n<ol>", "Synthetic nested alpha sublist stays tight without paragraph wrapper");
|
|
3072
|
-
apex_free_string(html);
|
|
3073
|
-
|
|
3074
|
-
/* Numeric parent list with indented alpha sublist → nested <ol style="lower-alpha"> */
|
|
3075
|
-
const char *numeric_with_nested_alpha = "1. One\n2. Two\n\ta. Two-One\n\tb. Two-Two\n3. Three\n";
|
|
3076
|
-
html = apex_markdown_to_html(numeric_with_nested_alpha, strlen(numeric_with_nested_alpha), &unified_opts);
|
|
3077
|
-
assert_contains(html, "<ol style=\"list-style-type: lower-alpha\">",
|
|
3078
|
-
"Alpha sublist under numeric list gets lower-alpha styling");
|
|
3079
|
-
assert_contains(html, "<li>Two-One</li>", "Nested alpha item text preserved");
|
|
3080
|
-
assert_not_contains(html, "<!-- apex-alpha-list-", "Alpha HTML comment marker stripped from output");
|
|
3081
|
-
apex_free_string(html);
|
|
3082
|
-
|
|
3083
|
-
const char *numeric_with_nested_ordered = "1. Test\n2. Test\n\t3. Test\n\t4. Test\n5. Test\n";
|
|
3084
|
-
html = apex_markdown_to_html(numeric_with_nested_ordered, strlen(numeric_with_nested_ordered), &unified_opts);
|
|
3085
|
-
assert_contains(html, "<ol start=\"3\">", "Numeric nested ordered sublist renders as nested ordered list");
|
|
3086
|
-
assert_not_contains(html, "2. Test\n 3. Test", "Nested ordered items are not flattened into parent text");
|
|
3087
|
-
assert_not_contains(html, "<p>Test</p>\n<ol start=\"3\">", "Synthetic nested ordered list stays tight without paragraph wrapper");
|
|
3088
|
-
apex_free_string(html);
|
|
3089
|
-
|
|
3090
|
-
const char *numeric_with_explicit_loose_nested_ordered = "1. Test\n2. Test\n\n\t3. Test\n\t4. Test\n5. Test\n";
|
|
3091
|
-
html = apex_markdown_to_html(numeric_with_explicit_loose_nested_ordered, strlen(numeric_with_explicit_loose_nested_ordered), &unified_opts);
|
|
3092
|
-
assert_contains(html, "<ol start=\"3\">", "Explicit blank line nested ordered sublist renders");
|
|
3093
|
-
assert_contains(html, "<p>Test</p>\n<ol start=\"3\">", "Explicit blank line keeps loose list paragraph wrapper");
|
|
3094
|
-
apex_free_string(html);
|
|
3095
|
-
|
|
3096
|
-
bool had_failures = suite_end(suite_failures);
|
|
3097
|
-
print_suite_title("Mixed List Markers Tests", had_failures, false);
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
|
-
/**
|
|
3101
|
-
* Test unsafe mode (raw HTML handling)
|
|
3102
|
-
*/
|
|
3103
|
-
|
|
3104
|
-
void test_unsafe_mode(void) {
|
|
3105
|
-
int suite_failures = suite_start();
|
|
3106
|
-
print_suite_title("Unsafe Mode Tests", false, true);
|
|
3107
|
-
|
|
3108
|
-
char *html;
|
|
3109
|
-
|
|
3110
|
-
/* Test that unsafe mode allows raw HTML by default in unified mode */
|
|
3111
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3112
|
-
const char *raw_html = "<div>Raw HTML content</div>";
|
|
3113
|
-
html = apex_markdown_to_html(raw_html, strlen(raw_html), &unified_opts);
|
|
3114
|
-
assert_contains(html, "<div>Raw HTML content</div>", "Raw HTML allowed in unified mode");
|
|
3115
|
-
if (strstr(html, "raw HTML omitted") == NULL && strstr(html, "omitted") == NULL) {
|
|
3116
|
-
test_result(true, "Raw HTML preserved in unified mode (unsafe default)");
|
|
3117
|
-
} else {
|
|
3118
|
-
test_result(false, "Raw HTML not preserved in unified mode");
|
|
3119
|
-
}
|
|
3120
|
-
apex_free_string(html);
|
|
3121
|
-
|
|
3122
|
-
/* Test that unsafe mode blocks raw HTML in CommonMark mode */
|
|
3123
|
-
apex_options cm_opts = apex_options_for_mode(APEX_MODE_COMMONMARK);
|
|
3124
|
-
html = apex_markdown_to_html(raw_html, strlen(raw_html), &cm_opts);
|
|
3125
|
-
if (strstr(html, "raw HTML omitted") != NULL || strstr(html, "omitted") != NULL) {
|
|
3126
|
-
tests_passed++;
|
|
3127
|
-
tests_run++;
|
|
3128
|
-
if (!errors_only_output) {
|
|
3129
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Raw HTML blocked in CommonMark mode (safe default)\n");
|
|
3130
|
-
}
|
|
3131
|
-
} else if (strstr(html, "<div>Raw HTML content</div>") == NULL) {
|
|
3132
|
-
tests_passed++;
|
|
3133
|
-
tests_run++;
|
|
3134
|
-
if (!errors_only_output) {
|
|
3135
|
-
printf(COLOR_GREEN "✓" COLOR_RESET " Raw HTML blocked in CommonMark mode (safe default)\n");
|
|
3136
|
-
}
|
|
3137
|
-
} else {
|
|
3138
|
-
tests_failed++;
|
|
3139
|
-
tests_run++;
|
|
3140
|
-
printf(COLOR_RED "✗" COLOR_RESET " Raw HTML not blocked in CommonMark mode\n");
|
|
3141
|
-
}
|
|
3142
|
-
apex_free_string(html);
|
|
3143
|
-
|
|
3144
|
-
/* Test that unsafe=false blocks raw HTML even in unified mode */
|
|
3145
|
-
unified_opts.unsafe = false;
|
|
3146
|
-
html = apex_markdown_to_html(raw_html, strlen(raw_html), &unified_opts);
|
|
3147
|
-
if (strstr(html, "raw HTML omitted") != NULL || strstr(html, "omitted") != NULL) {
|
|
3148
|
-
test_result(true, "--no-unsafe blocks raw HTML");
|
|
3149
|
-
} else if (strstr(html, "<div>Raw HTML content</div>") == NULL) {
|
|
3150
|
-
test_result(true, "--no-unsafe blocks raw HTML");
|
|
3151
|
-
} else {
|
|
3152
|
-
test_result(false, "--no-unsafe does not block raw HTML");
|
|
3153
|
-
}
|
|
3154
|
-
apex_free_string(html);
|
|
3155
|
-
|
|
3156
|
-
/* Test that unsafe=true allows raw HTML even in CommonMark mode */
|
|
3157
|
-
cm_opts.unsafe = true;
|
|
3158
|
-
html = apex_markdown_to_html(raw_html, strlen(raw_html), &cm_opts);
|
|
3159
|
-
assert_contains(html, "<div>Raw HTML content</div>", "Raw HTML allowed with unsafe=true");
|
|
3160
|
-
apex_free_string(html);
|
|
3161
|
-
|
|
3162
|
-
/* Test HTML comments in unsafe mode */
|
|
3163
|
-
const char *html_comment = "<!-- This is a comment -->";
|
|
3164
|
-
unified_opts.unsafe = true;
|
|
3165
|
-
html = apex_markdown_to_html(html_comment, strlen(html_comment), &unified_opts);
|
|
3166
|
-
assert_contains(html, "<!-- This is a comment -->", "HTML comments preserved in unsafe mode");
|
|
3167
|
-
apex_free_string(html);
|
|
3168
|
-
|
|
3169
|
-
/* Test HTML comments in safe mode */
|
|
3170
|
-
unified_opts.unsafe = false;
|
|
3171
|
-
html = apex_markdown_to_html(html_comment, strlen(html_comment), &unified_opts);
|
|
3172
|
-
if (strstr(html, "raw HTML omitted") != NULL || strstr(html, "omitted") != NULL) {
|
|
3173
|
-
test_result(true, "HTML comments blocked in safe mode");
|
|
3174
|
-
} else {
|
|
3175
|
-
test_result(false, "HTML comments not blocked in safe mode");
|
|
3176
|
-
}
|
|
3177
|
-
apex_free_string(html);
|
|
3178
|
-
|
|
3179
|
-
/* Test script tags are handled according to unsafe setting */
|
|
3180
|
-
const char *script_tag = "<script>alert('xss')</script>";
|
|
3181
|
-
unified_opts.unsafe = true;
|
|
3182
|
-
html = apex_markdown_to_html(script_tag, strlen(script_tag), &unified_opts);
|
|
3183
|
-
/* In unsafe mode, script tags might be preserved or escaped depending on cmark-gfm */
|
|
3184
|
-
/* Just verify it's handled somehow */
|
|
3185
|
-
if (strstr(html, "script") != NULL || strstr(html, "omitted") != NULL) {
|
|
3186
|
-
test_result(true, "Script tags handled in unsafe mode");
|
|
3187
|
-
} else {
|
|
3188
|
-
test_result(false, "Script tags not handled in unsafe mode");
|
|
3189
|
-
}
|
|
3190
|
-
apex_free_string(html);
|
|
3191
|
-
|
|
3192
|
-
bool had_failures = suite_end(suite_failures);
|
|
3193
|
-
print_suite_title("Unsafe Mode Tests", had_failures, false);
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
/**
|
|
3197
|
-
* Test Insert Syntax (++text++)
|
|
3198
|
-
*/
|
|
3199
|
-
void test_insert_syntax(void) {
|
|
3200
|
-
int suite_failures = suite_start();
|
|
3201
|
-
print_suite_title("Insert Syntax Tests", false, true);
|
|
3202
|
-
|
|
3203
|
-
apex_options opts = apex_options_default();
|
|
3204
|
-
char *html;
|
|
3205
|
-
|
|
3206
|
-
/* Test basic insert without IAL */
|
|
3207
|
-
html = apex_markdown_to_html("Text ++inserted++ here", 23, &opts);
|
|
3208
|
-
assert_contains(html, "<ins>inserted</ins>", "Basic insert syntax");
|
|
3209
|
-
apex_free_string(html);
|
|
3210
|
-
|
|
3211
|
-
/* Test insert with Kramdown-style IAL */
|
|
3212
|
-
html = apex_markdown_to_html("Text ++inserted++{: .class} here", 33, &opts);
|
|
3213
|
-
assert_contains(html, "<ins", "Insert with IAL creates ins tag");
|
|
3214
|
-
assert_contains(html, "class=\"class\"", "Insert with IAL has class");
|
|
3215
|
-
assert_contains(html, "inserted", "Insert with IAL contains text");
|
|
3216
|
-
apex_free_string(html);
|
|
3217
|
-
|
|
3218
|
-
/* Test insert with Pandoc-style IAL */
|
|
3219
|
-
html = apex_markdown_to_html("Text ++inserted++{#id .class} here", 35, &opts);
|
|
3220
|
-
assert_contains(html, "<ins", "Insert with Pandoc IAL creates ins tag");
|
|
3221
|
-
assert_contains(html, "id=\"id\"", "Insert with Pandoc IAL has ID");
|
|
3222
|
-
assert_contains(html, "class=\"class\"", "Insert with Pandoc IAL has class");
|
|
3223
|
-
apex_free_string(html);
|
|
3224
|
-
|
|
3225
|
-
/* Test insert with multiple classes */
|
|
3226
|
-
html = apex_markdown_to_html("Text ++inserted++{: .class1 .class2} here", 39, &opts);
|
|
3227
|
-
assert_contains(html, "class=\"class1 class2\"", "Insert with multiple classes");
|
|
3228
|
-
apex_free_string(html);
|
|
3229
|
-
|
|
3230
|
-
/* Test insert does not interfere with CriticMarkup */
|
|
3231
|
-
opts.enable_critic_markup = true;
|
|
3232
|
-
opts.critic_mode = 2; /* CRITIC_MARKUP */
|
|
3233
|
-
html = apex_markdown_to_html("Text {++critic++} and ++plain++ here", 38, &opts);
|
|
3234
|
-
assert_contains(html, "<ins class=\"critic\">critic</ins>", "CriticMarkup insert still works");
|
|
3235
|
-
assert_contains(html, "<ins>plain</ins>", "Plain insert still works");
|
|
3236
|
-
apex_free_string(html);
|
|
3237
|
-
|
|
3238
|
-
/* Test insert in code blocks is not processed */
|
|
3239
|
-
html = apex_markdown_to_html("```\n++code++\n```", 18, &opts);
|
|
3240
|
-
assert_contains(html, "++code++", "Insert in code block not processed");
|
|
3241
|
-
assert_not_contains(html, "<ins>code</ins>", "Insert in code block not converted");
|
|
3242
|
-
apex_free_string(html);
|
|
3243
|
-
|
|
3244
|
-
/* Test insert in inline code is not processed */
|
|
3245
|
-
html = apex_markdown_to_html("Text `++code++` here", 20, &opts);
|
|
3246
|
-
assert_contains(html, "++code++", "Insert in inline code not processed");
|
|
3247
|
-
assert_not_contains(html, "<ins>code</ins>", "Insert in inline code not converted");
|
|
3248
|
-
apex_free_string(html);
|
|
3249
|
-
|
|
3250
|
-
/* Test insert with markdown inside */
|
|
3251
|
-
html = apex_markdown_to_html("Text ++*italic*++ here", 23, &opts);
|
|
3252
|
-
assert_contains(html, "<ins>", "Insert tag present");
|
|
3253
|
-
assert_contains(html, "<em>italic</em>", "Markdown inside insert processed");
|
|
3254
|
-
apex_free_string(html);
|
|
3255
|
-
|
|
3256
|
-
/* Test insert with IAL and markdown inside */
|
|
3257
|
-
html = apex_markdown_to_html("Text ++*italic*++{: .highlight} here", 35, &opts);
|
|
3258
|
-
assert_contains(html, "<ins", "Insert with IAL and markdown creates ins tag");
|
|
3259
|
-
assert_contains(html, "class=\"highlight\"", "Insert with IAL has class");
|
|
3260
|
-
assert_contains(html, "<em>italic</em>", "Markdown inside insert with IAL processed");
|
|
3261
|
-
apex_free_string(html);
|
|
3262
|
-
|
|
3263
|
-
bool had_failures = suite_end(suite_failures);
|
|
3264
|
-
print_suite_title("Insert Syntax Tests", had_failures, false);
|
|
3265
|
-
}
|
|
3266
|
-
|
|
3267
|
-
/**
|
|
3268
|
-
* Test image captions -> figure/figcaption wrapping
|
|
3269
|
-
* Enabled by default in MultiMarkdown and Unified modes, configurable via options.
|
|
3270
|
-
*/
|
|
3271
|
-
void test_image_captions(void) {
|
|
3272
|
-
int suite_failures = suite_start();
|
|
3273
|
-
print_suite_title("Image Captions Tests", false, true);
|
|
3274
|
-
|
|
3275
|
-
const char *md_basic =
|
|
3276
|
-
"\n\n"
|
|
3277
|
-
"\n\n"
|
|
3278
|
-
"\n";
|
|
3279
|
-
|
|
3280
|
-
/* MultiMarkdown mode: captions enabled by default */
|
|
3281
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
3282
|
-
char *html = apex_markdown_to_html(md_basic, strlen(md_basic), &mmd_opts);
|
|
3283
|
-
assert_contains(html, "<figure>", "MMD: figures generated");
|
|
3284
|
-
assert_contains(html, "<img src=\"/img/basic.png\"", "MMD: basic image present");
|
|
3285
|
-
assert_contains(html, "<figcaption>Alt only</figcaption>", "MMD: caption from alt text");
|
|
3286
|
-
assert_contains(html, "<img src=\"/img/title.png\"", "MMD: titled image present");
|
|
3287
|
-
assert_contains(html, "<figcaption>Title caption</figcaption>", "MMD: caption from title text");
|
|
3288
|
-
assert_not_contains(html, "<figure><img src=\"/img/empty.png\"", "MMD: no figure for empty alt/title image");
|
|
3289
|
-
apex_free_string(html);
|
|
3290
|
-
|
|
3291
|
-
/* Unified mode: captions enabled by default */
|
|
3292
|
-
apex_options unified_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3293
|
-
html = apex_markdown_to_html(md_basic, strlen(md_basic), &unified_opts);
|
|
3294
|
-
assert_contains(html, "<figure>", "Unified: figures generated");
|
|
3295
|
-
assert_contains(html, "<figcaption>Alt only</figcaption>", "Unified: caption from alt text");
|
|
3296
|
-
assert_contains(html, "<figcaption>Title caption</figcaption>", "Unified: caption from title text");
|
|
3297
|
-
assert_not_contains(html, "<figure><img src=\"/img/empty.png\"", "Unified: no figure for empty alt/title image");
|
|
3298
|
-
apex_free_string(html);
|
|
3299
|
-
|
|
3300
|
-
/* Explicitly disabling captions should produce plain <img> tags */
|
|
3301
|
-
apex_options disabled_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3302
|
-
disabled_opts.enable_image_captions = false;
|
|
3303
|
-
html = apex_markdown_to_html(md_basic, strlen(md_basic), &disabled_opts);
|
|
3304
|
-
assert_not_contains(html, "<figure>", "Disabled: no figures generated");
|
|
3305
|
-
assert_not_contains(html, "<figcaption>", "Disabled: no figcaptions generated");
|
|
3306
|
-
assert_contains(html, "<img src=\"/img/basic.png\"", "Disabled: basic image present");
|
|
3307
|
-
apex_free_string(html);
|
|
3308
|
-
|
|
3309
|
-
/* caption="TEXT" on image adds figure/figcaption even when --image-captions is off */
|
|
3310
|
-
const char *md_caption_attr = "{caption=\"Explicit caption\"}\n";
|
|
3311
|
-
apex_options no_captions_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3312
|
-
no_captions_opts.enable_image_captions = false;
|
|
3313
|
-
html = apex_markdown_to_html(md_caption_attr, strlen(md_caption_attr), &no_captions_opts);
|
|
3314
|
-
assert_contains(html, "<figure>", "caption=: figure present");
|
|
3315
|
-
assert_contains(html, "<figcaption>Explicit caption</figcaption>", "caption=: figcaption text");
|
|
3316
|
-
assert_not_contains(html, "caption=\"", "caption=: caption attr stripped from img");
|
|
3317
|
-
apex_free_string(html);
|
|
3318
|
-
|
|
3319
|
-
/* --title-captions-only: only images with title get captions; alt-only images do not */
|
|
3320
|
-
const char *md_alt_and_title =
|
|
3321
|
-
"\n\n"
|
|
3322
|
-
"\n";
|
|
3323
|
-
apex_options title_only_opts = apex_options_for_mode(APEX_MODE_UNIFIED);
|
|
3324
|
-
title_only_opts.enable_image_captions = true;
|
|
3325
|
-
title_only_opts.title_captions_only = true;
|
|
3326
|
-
html = apex_markdown_to_html(md_alt_and_title, strlen(md_alt_and_title), &title_only_opts);
|
|
3327
|
-
assert_contains(html, "<figcaption>Title caption</figcaption>", "title_captions_only: caption from title");
|
|
3328
|
-
assert_not_contains(html, "<figcaption>Alt only</figcaption>", "title_captions_only: no caption from alt");
|
|
3329
|
-
apex_free_string(html);
|
|
3330
|
-
|
|
3331
|
-
bool had_failures = suite_end(suite_failures);
|
|
3332
|
-
print_suite_title("Image Captions Tests", had_failures, false);
|
|
3333
|
-
}
|
|
3334
|
-
|
|
3335
|
-
/**
|
|
3336
|
-
* Test image embedding
|
|
3337
|
-
*/
|