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,2056 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Output Tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
#include "test_helpers.h"
|
|
6
|
-
#include "apex/apex.h"
|
|
7
|
-
#include "../src/extensions/includes.h"
|
|
8
|
-
#include "../src/ast_json.h"
|
|
9
|
-
#include <string.h>
|
|
10
|
-
#include <stdlib.h>
|
|
11
|
-
#include <stdio.h>
|
|
12
|
-
|
|
13
|
-
void test_toc(void) {
|
|
14
|
-
int suite_failures = suite_start();
|
|
15
|
-
print_suite_title("TOC Generation Tests", false, true);
|
|
16
|
-
|
|
17
|
-
apex_options opts = apex_options_default();
|
|
18
|
-
opts.enable_marked_extensions = true;
|
|
19
|
-
char *html;
|
|
20
|
-
|
|
21
|
-
/* -t toc: Markdown list, default depth 1-3 */
|
|
22
|
-
apex_options toc_opts = apex_options_default();
|
|
23
|
-
toc_opts.output_format = APEX_OUTPUT_TOC;
|
|
24
|
-
const char *doc =
|
|
25
|
-
"# Introduction\n\n"
|
|
26
|
-
"## Getting Started\n\n"
|
|
27
|
-
"### Installation\n\n"
|
|
28
|
-
"#### Too Deep\n\n"
|
|
29
|
-
"## Configuration\n\n"
|
|
30
|
-
"# API\n"
|
|
31
|
-
"{: .no_toc}\n";
|
|
32
|
-
char *md = apex_markdown_to_html(doc, strlen(doc), &toc_opts);
|
|
33
|
-
assert_contains(md, "- [Introduction](#introduction)\n", "TOC md has H1");
|
|
34
|
-
assert_contains(md, " - [Getting Started](#getting-started)\n", "TOC md indents H2");
|
|
35
|
-
assert_contains(md, " - [Installation](#installation)\n", "TOC md indents H3");
|
|
36
|
-
assert_not_contains(md, "Too Deep", "TOC md excludes H4 by default");
|
|
37
|
-
assert_not_contains(md, "API", "TOC md excludes no_toc heading");
|
|
38
|
-
assert_not_contains(md, "<nav", "TOC md is not HTML");
|
|
39
|
-
apex_free_string(md);
|
|
40
|
-
|
|
41
|
-
toc_opts.toc_min = 2;
|
|
42
|
-
toc_opts.toc_max = 4;
|
|
43
|
-
md = apex_markdown_to_html(doc, strlen(doc), &toc_opts);
|
|
44
|
-
assert_not_contains(md, "Introduction", "toc-min-max excludes H1");
|
|
45
|
-
assert_contains(md, "Too Deep", "toc-min-max includes H4");
|
|
46
|
-
apex_free_string(md);
|
|
47
|
-
|
|
48
|
-
/* id-format mmd */
|
|
49
|
-
toc_opts = apex_options_default();
|
|
50
|
-
toc_opts.output_format = APEX_OUTPUT_TOC;
|
|
51
|
-
toc_opts.id_format = 1; /* MMD */
|
|
52
|
-
md = apex_markdown_to_html("# Hello World\n", 14, &toc_opts);
|
|
53
|
-
assert_contains(md, "- [Hello World](#helloworld)\n", "TOC md respects MMD id-format");
|
|
54
|
-
apex_free_string(md);
|
|
55
|
-
|
|
56
|
-
/* manual ID */
|
|
57
|
-
toc_opts = apex_options_default();
|
|
58
|
-
toc_opts.output_format = APEX_OUTPUT_TOC;
|
|
59
|
-
md = apex_markdown_to_html("# Custom {#my-custom}\n", 22, &toc_opts);
|
|
60
|
-
assert_contains(md, "- [Custom](#my-custom)\n", "TOC md uses manual heading ID");
|
|
61
|
-
apex_free_string(md);
|
|
62
|
-
|
|
63
|
-
/* id-format kramdown */
|
|
64
|
-
toc_opts = apex_options_default();
|
|
65
|
-
toc_opts.output_format = APEX_OUTPUT_TOC;
|
|
66
|
-
toc_opts.id_format = 2; /* Kramdown */
|
|
67
|
-
md = apex_markdown_to_html("# Trailing Dash-\n", 17, &toc_opts);
|
|
68
|
-
assert_contains(md, "- [Trailing Dash-](#trailing-dash-)\n", "TOC md respects Kramdown id-format");
|
|
69
|
-
apex_free_string(md);
|
|
70
|
-
|
|
71
|
-
/* empty */
|
|
72
|
-
toc_opts = apex_options_default();
|
|
73
|
-
toc_opts.output_format = APEX_OUTPUT_TOC;
|
|
74
|
-
md = apex_markdown_to_html("No headings here.\n", 18, &toc_opts);
|
|
75
|
-
if (md && (md[0] == '\0' || strcmp(md, "\n") == 0)) {
|
|
76
|
-
test_result(true, "TOC md empty when no headings");
|
|
77
|
-
} else {
|
|
78
|
-
test_result(false, "TOC md empty when no headings");
|
|
79
|
-
}
|
|
80
|
-
apex_free_string(md);
|
|
81
|
-
|
|
82
|
-
/* Bare {{TOC}} should use option defaults (1-3): exclude H4 */
|
|
83
|
-
const char *bare_default_depth =
|
|
84
|
-
"# H1\n\n{{TOC}}\n\n## H2\n\n### H3\n\n#### H4";
|
|
85
|
-
html = apex_markdown_to_html(bare_default_depth, strlen(bare_default_depth), &opts);
|
|
86
|
-
assert_contains(html, "href=\"#h1\"", "Bare TOC includes H1 by default");
|
|
87
|
-
assert_contains(html, "href=\"#h3\"", "Bare TOC includes H3 by default");
|
|
88
|
-
assert_not_contains(html, "href=\"#h4\"", "Bare TOC excludes H4 by default (max 3)");
|
|
89
|
-
apex_free_string(html);
|
|
90
|
-
|
|
91
|
-
/* --toc-min-max via options: 2,4 */
|
|
92
|
-
apex_options depth_opts = opts;
|
|
93
|
-
depth_opts.toc_min = 2;
|
|
94
|
-
depth_opts.toc_max = 4;
|
|
95
|
-
html = apex_markdown_to_html(bare_default_depth, strlen(bare_default_depth), &depth_opts);
|
|
96
|
-
assert_not_contains(html, "href=\"#h1\"", "toc_min=2 excludes H1");
|
|
97
|
-
assert_contains(html, "href=\"#h4\"", "toc_max=4 includes H4");
|
|
98
|
-
apex_free_string(html);
|
|
99
|
-
|
|
100
|
-
/* Marker override wins over options */
|
|
101
|
-
const char *override_toc =
|
|
102
|
-
"# H1\n\n{{TOC:1-6}}\n\n## H2\n\n### H3\n\n#### H4\n\n##### H5";
|
|
103
|
-
html = apex_markdown_to_html(override_toc, strlen(override_toc), &depth_opts);
|
|
104
|
-
assert_contains(html, "href=\"#h1\"", "Marker range overrides toc_min");
|
|
105
|
-
assert_contains(html, "href=\"#h5\"", "Marker range overrides toc_max");
|
|
106
|
-
apex_free_string(html);
|
|
107
|
-
|
|
108
|
-
/* Partial marker max under custom toc_min/toc_max: unspecified min is 1, not option min */
|
|
109
|
-
apex_options partial_opts = opts;
|
|
110
|
-
partial_opts.toc_min = 2;
|
|
111
|
-
partial_opts.toc_max = 4;
|
|
112
|
-
const char *partial_max =
|
|
113
|
-
"# H1\n\n<!--TOC max5-->\n\n## H2\n\n### H3\n\n#### H4\n\n##### H5\n\n###### H6";
|
|
114
|
-
html = apex_markdown_to_html(partial_max, strlen(partial_max), &partial_opts);
|
|
115
|
-
assert_contains(html, "href=\"#h1\"", "Partial max marker uses min=1 not option toc_min");
|
|
116
|
-
assert_contains(html, "href=\"#h5\"", "Partial max marker includes H5");
|
|
117
|
-
assert_not_contains(html, "href=\"#h6\"", "Partial max marker excludes H6");
|
|
118
|
-
apex_free_string(html);
|
|
119
|
-
|
|
120
|
-
/* Test basic TOC marker */
|
|
121
|
-
const char *doc_with_toc = "# Header 1\n\n<!--TOC-->\n\n## Header 2\n\n### Header 3";
|
|
122
|
-
html = apex_markdown_to_html(doc_with_toc, strlen(doc_with_toc), &opts);
|
|
123
|
-
assert_contains(html, "<ul", "TOC contains list");
|
|
124
|
-
assert_contains(html, "Header 1", "TOC includes H1");
|
|
125
|
-
assert_contains(html, "Header 2", "TOC includes H2");
|
|
126
|
-
assert_contains(html, "Header 3", "TOC includes H3");
|
|
127
|
-
apex_free_string(html);
|
|
128
|
-
|
|
129
|
-
/* Test MMD style TOC */
|
|
130
|
-
const char *mmd_toc = "# Title\n\n{{TOC}}\n\n## Section";
|
|
131
|
-
html = apex_markdown_to_html(mmd_toc, strlen(mmd_toc), &opts);
|
|
132
|
-
assert_contains(html, "<ul", "MMD TOC generates list");
|
|
133
|
-
assert_contains(html, "Section", "MMD TOC includes headers");
|
|
134
|
-
apex_free_string(html);
|
|
135
|
-
|
|
136
|
-
/* MultiMarkdown mode should process {{TOC}} even with marked extensions disabled */
|
|
137
|
-
apex_options mmd_opts = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
138
|
-
html = apex_markdown_to_html(mmd_toc, strlen(mmd_toc), &mmd_opts);
|
|
139
|
-
assert_contains(html, "<nav class=\"toc\">", "MMD mode renders TOC marker");
|
|
140
|
-
assert_contains(html, "Section", "MMD mode TOC includes headers");
|
|
141
|
-
apex_free_string(html);
|
|
142
|
-
|
|
143
|
-
/* Test TOC with depth range */
|
|
144
|
-
const char *depth_toc = "# H1\n\n{{TOC:2-3}}\n\n## H2\n\n### H3\n\n#### H4";
|
|
145
|
-
html = apex_markdown_to_html(depth_toc, strlen(depth_toc), &opts);
|
|
146
|
-
assert_contains(html, "<ul", "Depth-limited TOC generated");
|
|
147
|
-
assert_contains(html, "H2", "Includes H2");
|
|
148
|
-
assert_contains(html, "H3", "Includes H3");
|
|
149
|
-
/* H1 should be excluded (below min 2) */
|
|
150
|
-
/* H4 should be excluded (beyond max 3) */
|
|
151
|
-
if (strstr(html, "href=\"#h1\"") == NULL && strstr(html, "href=\"#h4\"") == NULL) {
|
|
152
|
-
test_result(true, "Depth range excludes H1 and H4");
|
|
153
|
-
} else {
|
|
154
|
-
test_result(false, "Depth range didn't exclude properly");
|
|
155
|
-
}
|
|
156
|
-
apex_free_string(html);
|
|
157
|
-
|
|
158
|
-
/* Regression: {{TOC:2}} must not parse beyond marker into later IDs */
|
|
159
|
-
const char *single_depth_toc =
|
|
160
|
-
"# Table of Contents Test\n\n{{TOC:2}}\n\nLorem ipsum\n\n## Section 1\n\n## Section 2\n\n## Section 3";
|
|
161
|
-
html = apex_markdown_to_html(single_depth_toc, strlen(single_depth_toc), &opts);
|
|
162
|
-
assert_contains(html, "<nav class=\"toc\">", "Single depth TOC nav rendered");
|
|
163
|
-
assert_contains(html, "href=\"#section-1\"", "Single depth TOC includes Section 1");
|
|
164
|
-
assert_contains(html, "href=\"#section-2\"", "Single depth TOC includes Section 2");
|
|
165
|
-
assert_contains(html, "href=\"#section-3\"", "Single depth TOC includes Section 3");
|
|
166
|
-
apex_free_string(html);
|
|
167
|
-
|
|
168
|
-
/* Regression: same-level headings in {{TOC:2-6}} should remain siblings */
|
|
169
|
-
const char *same_level_range_toc =
|
|
170
|
-
"# Table of Contents Test\n\n{{TOC:2-6}}\n\nLorem ipsum\n\n## Section 1\n\n## Section 2\n\n## Section 3";
|
|
171
|
-
html = apex_markdown_to_html(same_level_range_toc, strlen(same_level_range_toc), &opts);
|
|
172
|
-
assert_contains(html, "href=\"#section-1\"", "Range TOC includes Section 1");
|
|
173
|
-
assert_contains(html, "href=\"#section-2\"", "Range TOC includes Section 2");
|
|
174
|
-
assert_contains(html, "href=\"#section-3\"", "Range TOC includes Section 3");
|
|
175
|
-
assert_not_contains(html, "href=\"#section-1\">Section 1</a>\n <ul>",
|
|
176
|
-
"Range TOC does not nest same-level headings");
|
|
177
|
-
apex_free_string(html);
|
|
178
|
-
|
|
179
|
-
/* Regression: TOC labels should not include source indentation/newline whitespace */
|
|
180
|
-
const char *toc_whitespace =
|
|
181
|
-
"# Top\n\n{{TOC:2}}\n\n## Section 1\n\n##\tSection 2\n\n## Section 3";
|
|
182
|
-
html = apex_markdown_to_html(toc_whitespace, strlen(toc_whitespace), &opts);
|
|
183
|
-
assert_contains(html, "href=\"#section-1\">Section 1</a>", "TOC trims label whitespace");
|
|
184
|
-
assert_contains(html, "href=\"#section-2\">Section 2</a>", "TOC normalizes tab/newline whitespace");
|
|
185
|
-
assert_contains(html, "href=\"#section-3\">Section 3</a>", "TOC collapses internal whitespace runs");
|
|
186
|
-
apex_free_string(html);
|
|
187
|
-
|
|
188
|
-
/* Test TOC with max depth only */
|
|
189
|
-
const char *max_toc = "# H1\n\n<!--TOC max2-->\n\n## H2\n\n### H3";
|
|
190
|
-
html = apex_markdown_to_html(max_toc, strlen(max_toc), &opts);
|
|
191
|
-
assert_contains(html, "<ul", "Max depth TOC");
|
|
192
|
-
assert_contains(html, "H1", "Includes H1");
|
|
193
|
-
assert_contains(html, "H2", "Includes H2");
|
|
194
|
-
apex_free_string(html);
|
|
195
|
-
|
|
196
|
-
/* TOC inside inline code (backticks) must not be rendered */
|
|
197
|
-
const char *toc_inline_code = "# Title\n\nUse `{{TOC}}` in your template.\n\n## Section";
|
|
198
|
-
html = apex_markdown_to_html(toc_inline_code, strlen(toc_inline_code), &opts);
|
|
199
|
-
assert_contains(html, "<code>{{TOC}}</code>", "TOC in inline code is literal");
|
|
200
|
-
assert_contains(html, "Section", "Headers still in document");
|
|
201
|
-
apex_free_string(html);
|
|
202
|
-
|
|
203
|
-
/* TOC inside fenced code block must not be rendered */
|
|
204
|
-
const char *toc_code_block = "# Title\n\n```\n{{TOC}}\n```\n\n## Section";
|
|
205
|
-
html = apex_markdown_to_html(toc_code_block, strlen(toc_code_block), &opts);
|
|
206
|
-
assert_contains(html, "{{TOC}}", "TOC in code block is literal");
|
|
207
|
-
assert_contains(html, "<pre>", "Code block present");
|
|
208
|
-
apex_free_string(html);
|
|
209
|
-
|
|
210
|
-
/* First valid TOC (not in code) is used when one is in code and one is not */
|
|
211
|
-
const char *toc_then_real = "# Title\n\n`{{TOC}}`\n\n<!--TOC-->\n\n## Section";
|
|
212
|
-
html = apex_markdown_to_html(toc_then_real, strlen(toc_then_real), &opts);
|
|
213
|
-
assert_contains(html, "<code>{{TOC}}</code>", "TOC in code stays literal");
|
|
214
|
-
assert_contains(html, "<nav class=\"toc\">", "Real TOC marker is rendered");
|
|
215
|
-
assert_contains(html, "Section", "TOC includes section");
|
|
216
|
-
apex_free_string(html);
|
|
217
|
-
|
|
218
|
-
/* Escaped MMD TOC markers must not be expanded */
|
|
219
|
-
const char *escaped_toc =
|
|
220
|
-
"# Title\n\nMarked also recognizes MultiMarkdown-style \\{\\{TOC\\}\\}, "
|
|
221
|
-
"and Pandoc-style `{{TOC:2-6}}`.\n\n## Section";
|
|
222
|
-
html = apex_markdown_to_html(escaped_toc, strlen(escaped_toc), &opts);
|
|
223
|
-
assert_contains(html, "{{TOC}}", "Escaped MMD TOC marker preserved as literal text");
|
|
224
|
-
assert_not_contains(html, "<nav class=\"toc\">", "Escaped MMD TOC marker not expanded");
|
|
225
|
-
assert_contains(html, "<code>{{TOC:2-6}}</code>", "Pandoc-style TOC marker preserved in inline code");
|
|
226
|
-
apex_free_string(html);
|
|
227
|
-
|
|
228
|
-
/* Kramdown {:toc} inside indented code must not be converted */
|
|
229
|
-
apex_options kram_opts2 = apex_options_for_mode(APEX_MODE_KRAMDOWN);
|
|
230
|
-
kram_opts2.enable_marked_extensions = true;
|
|
231
|
-
const char *kramdown_toc_code = "# Title\n\n {:toc}\n\n## Section";
|
|
232
|
-
html = apex_markdown_to_html(kramdown_toc_code, strlen(kramdown_toc_code), &kram_opts2);
|
|
233
|
-
assert_contains(html, "{:toc}", "Kramdown {:toc} preserved in indented code");
|
|
234
|
-
assert_not_contains(html, "<nav class=\"toc\">", "Kramdown {:toc} not expanded in indented code");
|
|
235
|
-
apex_free_string(html);
|
|
236
|
-
|
|
237
|
-
/* Pandoc-style {{TOC:2-6}} inside fenced code must not be rendered */
|
|
238
|
-
const char *toc_range_fenced = "# Title\n\n```\n{{TOC:2-6}}\n```\n\n## Section\n\n### Sub";
|
|
239
|
-
html = apex_markdown_to_html(toc_range_fenced, strlen(toc_range_fenced), &opts);
|
|
240
|
-
assert_contains(html, "{{TOC:2-6}}", "Pandoc-style TOC marker preserved in fenced code");
|
|
241
|
-
assert_not_contains(html, "<nav class=\"toc\">", "Pandoc-style TOC marker not expanded in fenced code");
|
|
242
|
-
apex_free_string(html);
|
|
243
|
-
|
|
244
|
-
/* Pandoc-style {{TOC:2-6}} inside indented code must not be rendered */
|
|
245
|
-
const char *toc_range_indented = "# Title\n\n {{TOC:2-6}}\n\n## Section\n\n### Sub";
|
|
246
|
-
html = apex_markdown_to_html(toc_range_indented, strlen(toc_range_indented), &opts);
|
|
247
|
-
assert_contains(html, "{{TOC:2-6}}", "Pandoc-style TOC marker preserved in indented code");
|
|
248
|
-
assert_not_contains(html, "<nav class=\"toc\">", "Pandoc-style TOC marker not expanded in indented code");
|
|
249
|
-
apex_free_string(html);
|
|
250
|
-
|
|
251
|
-
/* HTML <!--TOC--> inside inline code must not be rendered */
|
|
252
|
-
html = apex_markdown_to_html("# Title\n\nUse `<!--TOC-->` here.\n\n## Section", 44, &opts);
|
|
253
|
-
assert_contains(html, "<code><!--TOC--></code>", "HTML TOC marker preserved in inline code");
|
|
254
|
-
assert_not_contains(html, "<nav class=\"toc\">", "HTML TOC marker not expanded in inline code");
|
|
255
|
-
apex_free_string(html);
|
|
256
|
-
|
|
257
|
-
/* Kramdown {:toc} inside fenced code must not be converted */
|
|
258
|
-
const char *kramdown_toc_fenced = "# Title\n\n```\n{:toc}\n```\n\n## Section";
|
|
259
|
-
html = apex_markdown_to_html(kramdown_toc_fenced, strlen(kramdown_toc_fenced), &kram_opts2);
|
|
260
|
-
assert_contains(html, "{:toc}", "Kramdown {:toc} preserved in fenced code");
|
|
261
|
-
assert_not_contains(html, "<nav class=\"toc\">", "Kramdown {:toc} not expanded in fenced code");
|
|
262
|
-
apex_free_string(html);
|
|
263
|
-
|
|
264
|
-
/* Test document without TOC marker */
|
|
265
|
-
const char *no_toc = "# Header\n\nContent";
|
|
266
|
-
html = apex_markdown_to_html(no_toc, strlen(no_toc), &opts);
|
|
267
|
-
assert_contains(html, "<h1", "Normal header without TOC");
|
|
268
|
-
assert_contains(html, "Header</h1>", "Normal header content");
|
|
269
|
-
apex_free_string(html);
|
|
270
|
-
|
|
271
|
-
/* Test nested header structure */
|
|
272
|
-
const char *nested = "# Top\n\n<!--TOC-->\n\n## Level 2A\n\n### Level 3\n\n## Level 2B";
|
|
273
|
-
html = apex_markdown_to_html(nested, strlen(nested), &opts);
|
|
274
|
-
assert_contains(html, "<ul", "Nested TOC structure");
|
|
275
|
-
assert_contains(html, "Level 2A", "First L2 in TOC");
|
|
276
|
-
assert_contains(html, "Level 2B", "Second L2 in TOC");
|
|
277
|
-
assert_contains(html, "Level 3", "L3 nested in TOC");
|
|
278
|
-
apex_free_string(html);
|
|
279
|
-
|
|
280
|
-
/* Kramdown-specific TOC syntax: {:toc} and {:.no_toc} */
|
|
281
|
-
apex_options kram_opts = apex_options_for_mode(APEX_MODE_KRAMDOWN);
|
|
282
|
-
/* Ensure marked extensions (including TOC) are enabled in Kramdown mode */
|
|
283
|
-
kram_opts.enable_marked_extensions = true;
|
|
284
|
-
|
|
285
|
-
/* Basic {:toc} replacement and .no_toc exclusion */
|
|
286
|
-
const char *kramdown_toc =
|
|
287
|
-
"# Contents\n"
|
|
288
|
-
"{:.no_toc}\n"
|
|
289
|
-
"\n"
|
|
290
|
-
"## Section One\n"
|
|
291
|
-
"\n"
|
|
292
|
-
"{:toc}\n"
|
|
293
|
-
"\n"
|
|
294
|
-
"### Subsection\n";
|
|
295
|
-
|
|
296
|
-
html = apex_markdown_to_html(kramdown_toc, strlen(kramdown_toc), &kram_opts);
|
|
297
|
-
assert_contains(html, "<nav class=\"toc\">", "Kramdown {:toc} generates TOC");
|
|
298
|
-
assert_contains(html, "Section One", "Kramdown TOC includes regular headings");
|
|
299
|
-
/* The 'Contents' heading should be excluded from TOC due to .no_toc */
|
|
300
|
-
if (strstr(html, "Contents") != NULL) {
|
|
301
|
-
/* It should appear in the document, but not inside the TOC nav.
|
|
302
|
-
* We perform a simple heuristic check: if 'Contents' only appears
|
|
303
|
-
* outside the <nav class=\"toc\"> block, treat it as success.
|
|
304
|
-
*/
|
|
305
|
-
const char *nav_start = strstr(html, "<nav class=\"toc\">");
|
|
306
|
-
const char *nav_end = nav_start ? strstr(nav_start, "</nav>") : NULL;
|
|
307
|
-
const char *contents_pos = strstr(html, "Contents");
|
|
308
|
-
bool in_nav = nav_start && nav_end && contents_pos >= nav_start && contents_pos <= nav_end;
|
|
309
|
-
if (!in_nav) {
|
|
310
|
-
test_result(true, "Kramdown .no_toc excludes heading from TOC");
|
|
311
|
-
} else {
|
|
312
|
-
test_result(false, "Kramdown .no_toc heading appeared in TOC");
|
|
313
|
-
}
|
|
314
|
-
} else {
|
|
315
|
-
tests_failed++;
|
|
316
|
-
tests_run++;
|
|
317
|
-
printf(COLOR_RED "✗" COLOR_RESET " Kramdown document did not contain 'Contents' heading\n");
|
|
318
|
-
}
|
|
319
|
-
apex_free_string(html);
|
|
320
|
-
|
|
321
|
-
/* {:toc} with max-depth option: support both max2 and max=2 forms */
|
|
322
|
-
const char *kramdown_toc_max =
|
|
323
|
-
"# Top\n"
|
|
324
|
-
"\n"
|
|
325
|
-
"## Level 2\n"
|
|
326
|
-
"\n"
|
|
327
|
-
"### Level 3\n"
|
|
328
|
-
"\n"
|
|
329
|
-
"{:toc max2}\n";
|
|
330
|
-
|
|
331
|
-
html = apex_markdown_to_html(kramdown_toc_max, strlen(kramdown_toc_max), &kram_opts);
|
|
332
|
-
assert_contains(html, "<nav class=\"toc\">", "Kramdown {:toc max2} generates TOC");
|
|
333
|
-
assert_contains(html, "Level 2", "Kramdown {:toc max2} includes Level 2");
|
|
334
|
-
/* Level 3 is beyond max2 and should not be linked in TOC */
|
|
335
|
-
if (strstr(html, "Level 3") == NULL ||
|
|
336
|
-
(strstr(html, "Level 3") && !strstr(html, "href=\"#level-3\""))) {
|
|
337
|
-
test_result(true, "Kramdown {:toc max2} respects max depth");
|
|
338
|
-
} else {
|
|
339
|
-
test_result(false, "Kramdown {:toc max2} did not apply max depth");
|
|
340
|
-
}
|
|
341
|
-
apex_free_string(html);
|
|
342
|
-
|
|
343
|
-
/* Manual {#id} must appear in TOC hrefs */
|
|
344
|
-
const char *manual_id_toc =
|
|
345
|
-
"# Custom {#my-custom}\n\n{{TOC}}\n\n## Other";
|
|
346
|
-
html = apex_markdown_to_html(manual_id_toc, strlen(manual_id_toc), &opts);
|
|
347
|
-
assert_contains(html, "href=\"#my-custom\"", "TOC uses manual heading ID");
|
|
348
|
-
apex_free_string(html);
|
|
349
|
-
|
|
350
|
-
/* Structured TOC entries API for library / Swift consumers */
|
|
351
|
-
{
|
|
352
|
-
const char *entries_doc =
|
|
353
|
-
"# Introduction\n\n"
|
|
354
|
-
"## Getting Started\n\n"
|
|
355
|
-
"### Installation\n\n"
|
|
356
|
-
"#### Too Deep\n\n"
|
|
357
|
-
"# Skip Me\n"
|
|
358
|
-
"{: .no_toc}\n";
|
|
359
|
-
size_t count = 0;
|
|
360
|
-
apex_toc_entry *entries = apex_markdown_to_toc_entries(
|
|
361
|
-
entries_doc, strlen(entries_doc), NULL, &count);
|
|
362
|
-
assert_option_bool(count == 3, true, "toc entries default depth count is 3");
|
|
363
|
-
if (entries && count >= 3) {
|
|
364
|
-
assert_option_bool(entries[0].level == 1, true, "toc entries[0] level is 1");
|
|
365
|
-
assert_option_string(entries[0].text, "Introduction", "toc entries[0] text");
|
|
366
|
-
assert_option_string(entries[0].id, "introduction", "toc entries[0] id");
|
|
367
|
-
assert_option_bool(entries[1].level == 2, true, "toc entries[1] level is 2");
|
|
368
|
-
assert_option_string(entries[1].id, "getting-started", "toc entries[1] id");
|
|
369
|
-
assert_option_bool(entries[2].level == 3, true, "toc entries[2] level is 3");
|
|
370
|
-
assert_option_string(entries[2].id, "installation", "toc entries[2] id");
|
|
371
|
-
} else {
|
|
372
|
-
test_result(false, "toc entries array populated");
|
|
373
|
-
}
|
|
374
|
-
apex_toc_entries_free(entries, count);
|
|
375
|
-
|
|
376
|
-
apex_options depth_opts = apex_options_default();
|
|
377
|
-
depth_opts.toc_min = 2;
|
|
378
|
-
depth_opts.toc_max = 4;
|
|
379
|
-
entries = apex_markdown_to_toc_entries(entries_doc, strlen(entries_doc),
|
|
380
|
-
&depth_opts, &count);
|
|
381
|
-
assert_option_bool(count == 3, true, "toc entries toc_min/max count");
|
|
382
|
-
if (entries && count >= 1) {
|
|
383
|
-
assert_option_bool(entries[0].level == 2, true, "toc entries respects toc_min");
|
|
384
|
-
assert_option_string(entries[count - 1].text, "Too Deep",
|
|
385
|
-
"toc entries includes H4 when toc_max=4");
|
|
386
|
-
}
|
|
387
|
-
apex_toc_entries_free(entries, count);
|
|
388
|
-
|
|
389
|
-
entries = apex_markdown_to_toc_entries("No headings.\n", 12, NULL, &count);
|
|
390
|
-
assert_option_bool(count == 0, true, "toc entries empty when no headings");
|
|
391
|
-
assert_option_bool(entries == NULL, true, "toc entries NULL when empty");
|
|
392
|
-
apex_toc_entries_free(entries, count);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
bool had_failures = suite_end(suite_failures);
|
|
396
|
-
print_suite_title("TOC Generation Tests", had_failures, false);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Test HTML markdown attributes
|
|
401
|
-
*/
|
|
402
|
-
|
|
403
|
-
void test_standalone_output(void) {
|
|
404
|
-
int suite_failures = suite_start();
|
|
405
|
-
print_suite_title("Standalone Document Output Tests", false, true);
|
|
406
|
-
|
|
407
|
-
apex_options opts = apex_options_default();
|
|
408
|
-
opts.standalone = true;
|
|
409
|
-
opts.document_title = "Test Document";
|
|
410
|
-
char *html;
|
|
411
|
-
|
|
412
|
-
/* Test basic standalone document */
|
|
413
|
-
html = apex_markdown_to_html("# Header\n\nContent", 18, &opts);
|
|
414
|
-
assert_contains(html, "<!DOCTYPE html>", "Doctype present");
|
|
415
|
-
assert_contains(html, "<html lang=\"en\">", "HTML tag with lang");
|
|
416
|
-
assert_contains(html, "<meta charset=\"UTF-8\">", "Charset meta tag");
|
|
417
|
-
assert_contains(html, "viewport", "Viewport meta tag");
|
|
418
|
-
assert_contains(html, "<title>Test Document</title>", "Title tag");
|
|
419
|
-
assert_contains(html, "<body>", "Body tag");
|
|
420
|
-
assert_contains(html, "</body>", "Closing body tag");
|
|
421
|
-
assert_contains(html, "</html>", "Closing html tag");
|
|
422
|
-
apex_free_string(html);
|
|
423
|
-
|
|
424
|
-
/* Test with custom stylesheet */
|
|
425
|
-
const char *css_paths[] = { "styles.css", NULL };
|
|
426
|
-
opts.stylesheet_paths = css_paths;
|
|
427
|
-
opts.stylesheet_count = 1;
|
|
428
|
-
html = apex_markdown_to_html("**Bold**", 8, &opts);
|
|
429
|
-
assert_contains(html, "<link rel=\"stylesheet\" href=\"styles.css\">", "CSS link tag");
|
|
430
|
-
/* Should not have inline styles when stylesheet is provided */
|
|
431
|
-
if (strstr(html, "<style>") == NULL) {
|
|
432
|
-
test_result(true, "No inline styles with external CSS");
|
|
433
|
-
} else {
|
|
434
|
-
test_result(false, "Inline styles present with external CSS");
|
|
435
|
-
}
|
|
436
|
-
apex_free_string(html);
|
|
437
|
-
|
|
438
|
-
/* Test default title */
|
|
439
|
-
opts.document_title = NULL;
|
|
440
|
-
opts.stylesheet_paths = NULL;
|
|
441
|
-
opts.stylesheet_count = 0;
|
|
442
|
-
html = apex_markdown_to_html("Content", 7, &opts);
|
|
443
|
-
assert_contains(html, "<title>Document</title>", "Default title");
|
|
444
|
-
apex_free_string(html);
|
|
445
|
-
|
|
446
|
-
/* Test inline styles when no stylesheet */
|
|
447
|
-
opts.stylesheet_paths = NULL;
|
|
448
|
-
opts.stylesheet_count = 0;
|
|
449
|
-
html = apex_markdown_to_html("Content", 7, &opts);
|
|
450
|
-
assert_contains(html, "<style>", "Default inline styles");
|
|
451
|
-
assert_contains(html, "font-family:", "Style rules present");
|
|
452
|
-
apex_free_string(html);
|
|
453
|
-
|
|
454
|
-
/* Test that non-standalone doesn't include document structure */
|
|
455
|
-
apex_options frag_opts = apex_options_default();
|
|
456
|
-
frag_opts.standalone = false;
|
|
457
|
-
html = apex_markdown_to_html("# Header", 8, &frag_opts);
|
|
458
|
-
if (strstr(html, "<!DOCTYPE") == NULL && strstr(html, "<body>") == NULL) {
|
|
459
|
-
test_result(true, "Fragment mode doesn't include document structure");
|
|
460
|
-
} else {
|
|
461
|
-
test_result(false, "Fragment mode has document structure");
|
|
462
|
-
}
|
|
463
|
-
apex_free_string(html);
|
|
464
|
-
|
|
465
|
-
bool had_failures = suite_end(suite_failures);
|
|
466
|
-
print_suite_title("Standalone Document Output Tests", had_failures, false);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Test pretty HTML output
|
|
471
|
-
*/
|
|
472
|
-
|
|
473
|
-
void test_pretty_html(void) {
|
|
474
|
-
int suite_failures = suite_start();
|
|
475
|
-
print_suite_title("Pretty HTML Output Tests", false, true);
|
|
476
|
-
|
|
477
|
-
apex_options opts = apex_options_default();
|
|
478
|
-
opts.pretty = true;
|
|
479
|
-
opts.relaxed_tables = false; /* Use standard tables for pretty HTML tests */
|
|
480
|
-
char *html;
|
|
481
|
-
|
|
482
|
-
/* Test basic pretty formatting */
|
|
483
|
-
html = apex_markdown_to_html("# Header\n\nPara", 14, &opts);
|
|
484
|
-
/* Check for indentation and newlines */
|
|
485
|
-
assert_contains(html, "<h1", "Opening tag present");
|
|
486
|
-
assert_contains(html, ">\n", "Opening tag on own line");
|
|
487
|
-
assert_contains(html, "</h1>\n", "Closing tag on own line");
|
|
488
|
-
assert_contains(html, " Header", "Content indented");
|
|
489
|
-
apex_free_string(html);
|
|
490
|
-
|
|
491
|
-
/* Test nested structure (list) */
|
|
492
|
-
html = apex_markdown_to_html("- Item 1\n- Item 2", 17, &opts);
|
|
493
|
-
assert_contains(html, "<ul>\n", "List opening formatted");
|
|
494
|
-
assert_contains(html, " <li>", "List item indented");
|
|
495
|
-
assert_contains(html, "</ul>", "List closing formatted");
|
|
496
|
-
apex_free_string(html);
|
|
497
|
-
|
|
498
|
-
/* Test inline elements stay inline */
|
|
499
|
-
html = apex_markdown_to_html("Text with **bold**", 18, &opts);
|
|
500
|
-
assert_contains(html, "<strong>bold</strong>", "Inline elements not split");
|
|
501
|
-
apex_free_string(html);
|
|
502
|
-
|
|
503
|
-
/* TOC links should not get indentation whitespace inside anchor text */
|
|
504
|
-
html = apex_markdown_to_html("# Top\n\n{{TOC:2}}\n\n## Section 1", 31, &opts);
|
|
505
|
-
assert_contains(html, "<a href=\"#section-1\">Section 1</a>", "Pretty mode keeps TOC link text clean");
|
|
506
|
-
assert_not_contains(html, "<a href=\"#section-1\"> Section 1</a>",
|
|
507
|
-
"Pretty mode does not pad TOC link text");
|
|
508
|
-
apex_free_string(html);
|
|
509
|
-
|
|
510
|
-
/* Test table formatting */
|
|
511
|
-
const char *table = "| A | B |\n|---|---|\n| C | D |";
|
|
512
|
-
html = apex_markdown_to_html(table, strlen(table), &opts);
|
|
513
|
-
assert_contains(html, "<table>\n", "Table formatted");
|
|
514
|
-
assert_contains(html, " <thead>", "Table sections indented");
|
|
515
|
-
assert_contains(html, " <tr>", "Table rows further indented");
|
|
516
|
-
apex_free_string(html);
|
|
517
|
-
|
|
518
|
-
/* Test that non-pretty mode is compact */
|
|
519
|
-
apex_options compact_opts = apex_options_default();
|
|
520
|
-
compact_opts.pretty = false;
|
|
521
|
-
html = apex_markdown_to_html("# H\n\nP", 7, &compact_opts);
|
|
522
|
-
/* Should not have extra indentation */
|
|
523
|
-
if (strstr(html, " H") == NULL) {
|
|
524
|
-
test_result(true, "Compact mode has no indentation");
|
|
525
|
-
} else {
|
|
526
|
-
test_result(false, "Compact mode has indentation");
|
|
527
|
-
}
|
|
528
|
-
apex_free_string(html);
|
|
529
|
-
|
|
530
|
-
bool had_failures = suite_end(suite_failures);
|
|
531
|
-
print_suite_title("Pretty HTML Output Tests", had_failures, false);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Test --xhtml / --strict-xhtml HTML serialization
|
|
536
|
-
*/
|
|
537
|
-
void test_xhtml_output(void) {
|
|
538
|
-
int suite_failures = suite_start();
|
|
539
|
-
print_suite_title("XHTML Output Mode Tests", false, true);
|
|
540
|
-
|
|
541
|
-
apex_options opts = apex_options_default();
|
|
542
|
-
char *html;
|
|
543
|
-
const char *hr_md = "\n\n---\n\n";
|
|
544
|
-
|
|
545
|
-
/* --xhtml: self-closing void tags in fragment */
|
|
546
|
-
opts.xhtml = true;
|
|
547
|
-
html = apex_markdown_to_html(hr_md, strlen(hr_md), &opts);
|
|
548
|
-
assert_contains(html, "<hr />", "XHTML mode self-closes hr");
|
|
549
|
-
apex_free_string(html);
|
|
550
|
-
|
|
551
|
-
/* --xhtml standalone: charset meta self-closed, HTML5 doctype */
|
|
552
|
-
opts = apex_options_default();
|
|
553
|
-
opts.xhtml = true;
|
|
554
|
-
opts.standalone = true;
|
|
555
|
-
opts.document_title = "X";
|
|
556
|
-
html = apex_markdown_to_html("Hi", 2, &opts);
|
|
557
|
-
assert_contains(html, "<meta charset=\"UTF-8\" />", "XHTML standalone charset meta self-closed");
|
|
558
|
-
assert_contains(html, "<!DOCTYPE html>", "Still HTML5 doctype");
|
|
559
|
-
apex_free_string(html);
|
|
560
|
-
|
|
561
|
-
/* --strict-xhtml standalone: polyglot document */
|
|
562
|
-
opts = apex_options_default();
|
|
563
|
-
opts.strict_xhtml = true;
|
|
564
|
-
opts.standalone = true;
|
|
565
|
-
opts.document_title = "S";
|
|
566
|
-
html = apex_markdown_to_html("Hi", 2, &opts);
|
|
567
|
-
assert_contains(html, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "XML declaration");
|
|
568
|
-
assert_contains(html, "xmlns=\"http://www.w3.org/1999/xhtml\"", "XHTML namespace");
|
|
569
|
-
assert_contains(html, "application/xhtml+xml", "Strict Content-Type meta");
|
|
570
|
-
if (strstr(html, "<meta charset=") != NULL) {
|
|
571
|
-
test_result(false, "Strict mode should not emit separate charset meta");
|
|
572
|
-
} else {
|
|
573
|
-
test_result(true, "Strict mode uses Content-Type only for charset");
|
|
574
|
-
}
|
|
575
|
-
apex_free_string(html);
|
|
576
|
-
|
|
577
|
-
/* strict implies void serialization on fragments */
|
|
578
|
-
opts = apex_options_default();
|
|
579
|
-
opts.strict_xhtml = true;
|
|
580
|
-
html = apex_markdown_to_html(hr_md, strlen(hr_md), &opts);
|
|
581
|
-
assert_contains(html, "<hr />", "Strict mode self-closes void elements");
|
|
582
|
-
apex_free_string(html);
|
|
583
|
-
|
|
584
|
-
bool had_failures = suite_end(suite_failures);
|
|
585
|
-
print_suite_title("XHTML Output Mode Tests", had_failures, false);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Test terminal and terminal256 output format: ANSI output, list markers, and terminal_width option.
|
|
590
|
-
*/
|
|
591
|
-
void test_terminal_output(void) {
|
|
592
|
-
int suite_failures = suite_start();
|
|
593
|
-
print_suite_title("Terminal Output Tests", false, true);
|
|
594
|
-
|
|
595
|
-
apex_options opts = apex_options_default();
|
|
596
|
-
char *out;
|
|
597
|
-
|
|
598
|
-
/* Terminal format produces ANSI and content */
|
|
599
|
-
opts.output_format = APEX_OUTPUT_TERMINAL;
|
|
600
|
-
out = apex_markdown_to_html("# Hello", 7, &opts);
|
|
601
|
-
assert_contains(out, "\033", "Terminal output contains ANSI escape");
|
|
602
|
-
assert_contains(out, "Hello", "Terminal output contains heading text");
|
|
603
|
-
apex_free_string(out);
|
|
604
|
-
|
|
605
|
-
/* terminal256 also produces ANSI */
|
|
606
|
-
opts.output_format = APEX_OUTPUT_TERMINAL256;
|
|
607
|
-
out = apex_markdown_to_html("**bold** text", 13, &opts);
|
|
608
|
-
assert_contains(out, "\033", "Terminal256 output contains ANSI");
|
|
609
|
-
assert_contains(out, "bold", "Terminal256 output contains bold text");
|
|
610
|
-
apex_free_string(out);
|
|
611
|
-
|
|
612
|
-
/* Bullet list: default list_marker yields bullet and item text */
|
|
613
|
-
opts.output_format = APEX_OUTPUT_TERMINAL;
|
|
614
|
-
out = apex_markdown_to_html("- one\n- two", 11, &opts);
|
|
615
|
-
assert_contains(out, "* ", "Terminal bullet list contains marker");
|
|
616
|
-
assert_contains(out, "one", "Terminal list contains first item");
|
|
617
|
-
assert_contains(out, "two", "Terminal list contains second item");
|
|
618
|
-
apex_free_string(out);
|
|
619
|
-
|
|
620
|
-
/* Ordered list: same list_marker styling, numbered labels */
|
|
621
|
-
out = apex_markdown_to_html("1. first\n2. second", 18, &opts);
|
|
622
|
-
assert_contains(out, "1.", "Terminal ordered list contains first number");
|
|
623
|
-
assert_contains(out, "2.", "Terminal ordered list contains second number");
|
|
624
|
-
assert_contains(out, "first", "Terminal ordered list contains first item text");
|
|
625
|
-
assert_contains(out, "second", "Terminal ordered list contains second item text");
|
|
626
|
-
apex_free_string(out);
|
|
627
|
-
|
|
628
|
-
/* terminal_width in options does not break output (wrapping is applied by CLI) */
|
|
629
|
-
opts.terminal_width = 40;
|
|
630
|
-
out = apex_markdown_to_html("plain paragraph", 15, &opts);
|
|
631
|
-
test_result(out != NULL && strstr(out, "plain") != NULL, "terminal_width set still produces terminal output");
|
|
632
|
-
if (out) apex_free_string(out);
|
|
633
|
-
|
|
634
|
-
/* apex_resolve_local_image_path */
|
|
635
|
-
{
|
|
636
|
-
char *rp = apex_resolve_local_image_path("img/a.png", "/tmp/proj");
|
|
637
|
-
test_result(rp != NULL && strcmp(rp, "/tmp/proj/img/a.png") == 0,
|
|
638
|
-
"apex_resolve_local_image_path joins base_directory");
|
|
639
|
-
free(rp);
|
|
640
|
-
rp = apex_resolve_local_image_path("/abs/foo.png", "/any");
|
|
641
|
-
test_result(rp != NULL && strcmp(rp, "/abs/foo.png") == 0,
|
|
642
|
-
"apex_resolve_local_image_path keeps absolute paths");
|
|
643
|
-
free(rp);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/* Terminal images: inline rendering uses isatty(STDOUT_FILENO). Test output is
|
|
647
|
-
* captured to a string; under a normal CI pipe or non-TTY, stdout is not a TTY
|
|
648
|
-
* so these exercises never call curl or imgcat/chafa/viu/catimg. (Running the
|
|
649
|
-
* suite in an interactive terminal with those tools on PATH could take a
|
|
650
|
-
* different path for remote URLs.) */
|
|
651
|
-
|
|
652
|
-
/* Remote images: link-style fallback when not inline (non-TTY: no download/viewer) */
|
|
653
|
-
opts = apex_options_default();
|
|
654
|
-
opts.output_format = APEX_OUTPUT_TERMINAL;
|
|
655
|
-
out = apex_markdown_to_html("", 28, &opts);
|
|
656
|
-
assert_contains(out, "https://ex.com/x.png", "Remote image URL in fallback");
|
|
657
|
-
test_result(strstr(out, "", 26, &opts);
|
|
664
|
-
assert_contains(out, "http://ex.com/y.png", "http remote image URL in fallback");
|
|
665
|
-
test_result(strstr(out, "", 28, &opts);
|
|
672
|
-
assert_contains(out, "https://ex.com/z.png", "terminal256 remote image URL in fallback");
|
|
673
|
-
test_result(strstr(out, "", 25, &opts);
|
|
680
|
-
assert_contains(out, "https://ex.com/e.png", "Empty-alt remote image URL in fallback");
|
|
681
|
-
test_result(strstr(out, "", 28, &opts);
|
|
689
|
-
test_result(out != NULL && strstr(out, "https://ex.com/w.png") != NULL,
|
|
690
|
-
"terminal_image_width set with non-TTY still yields link-style remote image");
|
|
691
|
-
apex_free_string(out);
|
|
692
|
-
|
|
693
|
-
/* terminal_inline_images false: link-style fallback */
|
|
694
|
-
opts = apex_options_default();
|
|
695
|
-
opts.output_format = APEX_OUTPUT_TERMINAL;
|
|
696
|
-
opts.terminal_inline_images = false;
|
|
697
|
-
out = apex_markdown_to_html("", 17, &opts);
|
|
698
|
-
assert_contains(out, "local.png", "Disabled inline: URL shown like a link");
|
|
699
|
-
test_result(strstr(out, "", 45, &opts);
|
|
706
|
-
assert_contains(out, "apex_test_missing_image_99.png", "Missing file: URL in link-style output");
|
|
707
|
-
test_result(strstr(out, "", 17, &opts);
|
|
716
|
-
test_result(out != NULL, "paginate_symbols terminal256 produces output");
|
|
717
|
-
apex_free_string(out);
|
|
718
|
-
|
|
719
|
-
bool had_failures = suite_end(suite_failures);
|
|
720
|
-
print_suite_title("Terminal Output Tests", had_failures, false);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* Test RTF output
|
|
725
|
-
*/
|
|
726
|
-
void test_rtf_output(void) {
|
|
727
|
-
int suite_failures = suite_start();
|
|
728
|
-
print_suite_title("RTF Output Tests", false, true);
|
|
729
|
-
|
|
730
|
-
apex_options opts = apex_options_default();
|
|
731
|
-
opts.output_format = APEX_OUTPUT_RTF;
|
|
732
|
-
char *out;
|
|
733
|
-
|
|
734
|
-
const char *md =
|
|
735
|
-
"# Title\n\n"
|
|
736
|
-
"A paragraph with **bold**, *italic*, ~~strike~~, and `code`.\n\n"
|
|
737
|
-
"Visit [Example](https://example.com).\n\n"
|
|
738
|
-
"- Item one\n"
|
|
739
|
-
"- Item two\n\n"
|
|
740
|
-
"> A quote\n\n"
|
|
741
|
-
"```\ncode block\n```\n\n"
|
|
742
|
-
"| A | B |\n| --- | --- |\n| 1 | 2 |\n\n"
|
|
743
|
-
"H~2~O and E=mc^2^.\n\n"
|
|
744
|
-
"---\n";
|
|
745
|
-
|
|
746
|
-
out = apex_markdown_to_html(md, strlen(md), &opts);
|
|
747
|
-
assert_contains(out, "{\\rtf1", "RTF has document header");
|
|
748
|
-
assert_contains(out, "\\fonttbl", "RTF has font table");
|
|
749
|
-
assert_contains(out, "Title", "RTF includes heading text");
|
|
750
|
-
assert_contains(out, "\\b ", "RTF has bold");
|
|
751
|
-
assert_contains(out, "\\i ", "RTF has italic");
|
|
752
|
-
assert_contains(out, "\\strike", "RTF has strikethrough");
|
|
753
|
-
assert_contains(out, "\\f1", "RTF uses mono font for code");
|
|
754
|
-
assert_contains(out, "HYPERLINK", "RTF has hyperlink field");
|
|
755
|
-
assert_contains(out, "example.com", "RTF link URL present");
|
|
756
|
-
assert_contains(out, "\\bullet", "RTF has bullet list");
|
|
757
|
-
assert_contains(out, "\\trowd", "RTF has table row");
|
|
758
|
-
assert_contains(out, "code block", "RTF has code block text");
|
|
759
|
-
assert_contains(out, "\\super", "RTF has superscript");
|
|
760
|
-
assert_contains(out, "\\sub", "RTF has subscript");
|
|
761
|
-
test_result(out != NULL && strstr(out, "}") != NULL, "RTF document contains closing brace");
|
|
762
|
-
apex_free_string(out);
|
|
763
|
-
|
|
764
|
-
/* Ordered list alone so mixed-list merging cannot absorb it into a ul */
|
|
765
|
-
out = apex_markdown_to_html("1. First\n2. Second\n", 20, &opts);
|
|
766
|
-
assert_contains(out, "1. ", "RTF has ordered list marker");
|
|
767
|
-
assert_contains(out, "First", "RTF ordered list item text");
|
|
768
|
-
apex_free_string(out);
|
|
769
|
-
|
|
770
|
-
/* Footnotes endnotes */
|
|
771
|
-
const char *fn = "Note.[^1]\n\n[^1]: Footnote body.\n";
|
|
772
|
-
out = apex_markdown_to_html(fn, strlen(fn), &opts);
|
|
773
|
-
assert_contains(out, "{\\rtf1", "Footnote doc is RTF");
|
|
774
|
-
assert_contains(out, "Footnotes", "RTF endnotes section");
|
|
775
|
-
assert_contains(out, "Footnote body", "RTF footnote body");
|
|
776
|
-
apex_free_string(out);
|
|
777
|
-
|
|
778
|
-
/* Direct serializer */
|
|
779
|
-
out = apex_markdown_to_html("Hi", 2, &opts);
|
|
780
|
-
test_result(out != NULL && strncmp(out, "{\\rtf1", 6) == 0, "apex_markdown_to_html RTF path");
|
|
781
|
-
apex_free_string(out);
|
|
782
|
-
|
|
783
|
-
/* Remote images: alt becomes a hyperlink; no raw URL dump in body */
|
|
784
|
-
{
|
|
785
|
-
const char *remote =
|
|
786
|
-
"\n";
|
|
787
|
-
out = apex_markdown_to_html(remote, strlen(remote), &opts);
|
|
788
|
-
assert_contains(out, "HYPERLINK", "Remote image uses HYPERLINK fallback");
|
|
789
|
-
assert_contains(out, "Version: 1.1.12", "Remote image shows alt text");
|
|
790
|
-
assert_contains(out, "img.shields.io", "Remote image URL in HYPERLINK field");
|
|
791
|
-
test_result(out != NULL && strstr(out, "] (") == NULL, "Remote image does not dump ] (url)");
|
|
792
|
-
test_result(out != NULL && strstr(out, "\\pict") == NULL, "Remote image is not embedded as pict");
|
|
793
|
-
apex_free_string(out);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
{
|
|
797
|
-
const char *empty_alt = "\n";
|
|
798
|
-
out = apex_markdown_to_html(empty_alt, strlen(empty_alt), &opts);
|
|
799
|
-
assert_contains(out, "HYPERLINK", "Empty-alt remote image is still a link");
|
|
800
|
-
assert_contains(out, "image", "Empty-alt remote image uses image label");
|
|
801
|
-
apex_free_string(out);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/* Local JPEG embeds without embed_images */
|
|
805
|
-
opts.embed_images = false;
|
|
806
|
-
opts.base_directory = "tests/fixtures/images";
|
|
807
|
-
{
|
|
808
|
-
const char *local = "\n";
|
|
809
|
-
out = apex_markdown_to_html(local, strlen(local), &opts);
|
|
810
|
-
assert_contains(out, "\\pict", "Local JPEG embeds as RTF pict");
|
|
811
|
-
assert_contains(out, "jpegblip", "Local JPEG uses jpegblip");
|
|
812
|
-
test_result(out != NULL && strstr(out, "HYPERLINK") == NULL,
|
|
813
|
-
"Embedded local image is not a hyperlink fallback");
|
|
814
|
-
apex_free_string(out);
|
|
815
|
-
}
|
|
816
|
-
opts.base_directory = NULL;
|
|
817
|
-
|
|
818
|
-
bool had_failures = suite_end(suite_failures);
|
|
819
|
-
print_suite_title("RTF Output Tests", had_failures, false);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Test header ID generation
|
|
824
|
-
*/
|
|
825
|
-
|
|
826
|
-
void test_header_ids(void) {
|
|
827
|
-
int suite_failures = suite_start();
|
|
828
|
-
print_suite_title("Header ID Generation Tests", false, true);
|
|
829
|
-
|
|
830
|
-
apex_options opts = apex_options_default();
|
|
831
|
-
char *html;
|
|
832
|
-
|
|
833
|
-
/* Test default GFM format (with dashes) */
|
|
834
|
-
html = apex_markdown_to_html("# Emoji Support\n## Test Heading", 33, &opts);
|
|
835
|
-
assert_contains(html, "id=\"emoji-support\"", "GFM format: emoji-support");
|
|
836
|
-
assert_contains(html, "id=\"test-heading\"", "GFM format: test-heading");
|
|
837
|
-
apex_free_string(html);
|
|
838
|
-
|
|
839
|
-
/* Test GFM format converts emojis to names (Pandoc GFM behavior) */
|
|
840
|
-
opts.id_format = 0; /* GFM format */
|
|
841
|
-
opts.enable_marked_extensions = true; /* Enable emoji support */
|
|
842
|
-
const char *emoji_header_test = "# 😄 Emoji Support";
|
|
843
|
-
html = apex_markdown_to_html(emoji_header_test, strlen(emoji_header_test), &opts);
|
|
844
|
-
assert_contains(html, "id=\"smile-emoji-support\"", "GFM format converts emoji to name");
|
|
845
|
-
apex_free_string(html);
|
|
846
|
-
|
|
847
|
-
const char *emoji_only_test = "# 🚀";
|
|
848
|
-
html = apex_markdown_to_html(emoji_only_test, strlen(emoji_only_test), &opts);
|
|
849
|
-
assert_contains(html, "id=\"rocket\"", "GFM format converts single emoji to name");
|
|
850
|
-
apex_free_string(html);
|
|
851
|
-
|
|
852
|
-
const char *emoji_multiple_test = "# 👍 👎";
|
|
853
|
-
html = apex_markdown_to_html(emoji_multiple_test, strlen(emoji_multiple_test), &opts);
|
|
854
|
-
assert_contains(html, "id=\"thumbsup-thumbsdown\"", "GFM format converts multiple emojis to names");
|
|
855
|
-
apex_free_string(html);
|
|
856
|
-
|
|
857
|
-
/* Test MMD format (preserves dashes, removes spaces) */
|
|
858
|
-
opts.id_format = 1; /* MMD format */
|
|
859
|
-
html = apex_markdown_to_html("# Emoji Support\n## Test Heading", 33, &opts);
|
|
860
|
-
assert_contains(html, "id=\"emojisupport\"", "MMD format: emojisupport (spaces removed)");
|
|
861
|
-
assert_contains(html, "id=\"testheading\"", "MMD format: testheading (spaces removed)");
|
|
862
|
-
apex_free_string(html);
|
|
863
|
-
|
|
864
|
-
/* Test MMD format preserves dashes */
|
|
865
|
-
const char *mmd_dash_test = "# header-one";
|
|
866
|
-
html = apex_markdown_to_html(mmd_dash_test, strlen(mmd_dash_test), &opts);
|
|
867
|
-
assert_contains(html, "id=\"header-one\"", "MMD format preserves regular dash");
|
|
868
|
-
apex_free_string(html);
|
|
869
|
-
|
|
870
|
-
const char *mmd_em_dash_test = "# header—one";
|
|
871
|
-
html = apex_markdown_to_html(mmd_em_dash_test, strlen(mmd_em_dash_test), &opts);
|
|
872
|
-
assert_contains(html, "id=\"header—one\"", "MMD format preserves em dash");
|
|
873
|
-
apex_free_string(html);
|
|
874
|
-
|
|
875
|
-
const char *mmd_en_dash_test = "# header–one";
|
|
876
|
-
html = apex_markdown_to_html(mmd_en_dash_test, strlen(mmd_en_dash_test), &opts);
|
|
877
|
-
assert_contains(html, "id=\"header–one\"", "MMD format preserves en dash");
|
|
878
|
-
apex_free_string(html);
|
|
879
|
-
|
|
880
|
-
/* Test MMD format preserves leading/trailing dashes */
|
|
881
|
-
const char *mmd_leading_test = "# -Leading";
|
|
882
|
-
html = apex_markdown_to_html(mmd_leading_test, strlen(mmd_leading_test), &opts);
|
|
883
|
-
assert_contains(html, "id=\"-leading\"", "MMD format preserves leading dash");
|
|
884
|
-
apex_free_string(html);
|
|
885
|
-
|
|
886
|
-
const char *mmd_trailing_test = "# Trailing-";
|
|
887
|
-
html = apex_markdown_to_html(mmd_trailing_test, strlen(mmd_trailing_test), &opts);
|
|
888
|
-
assert_contains(html, "id=\"trailing-\"", "MMD format preserves trailing dash");
|
|
889
|
-
apex_free_string(html);
|
|
890
|
-
|
|
891
|
-
/* Test MMD format preserves diacritics */
|
|
892
|
-
const char *mmd_diacritics_test = "# Émoji Support";
|
|
893
|
-
html = apex_markdown_to_html(mmd_diacritics_test, strlen(mmd_diacritics_test), &opts);
|
|
894
|
-
assert_contains(html, "id=\"Émojisupport\"", "MMD format preserves diacritics");
|
|
895
|
-
apex_free_string(html);
|
|
896
|
-
|
|
897
|
-
/* Test MMD format removes apostrophes (curly and straight) - they break anchor links */
|
|
898
|
-
const char *mmd_apostrophe_test = "# What\xE2\x80\x99s Markdown?"; /* curly apostrophe U+2019 */
|
|
899
|
-
html = apex_markdown_to_html(mmd_apostrophe_test, strlen(mmd_apostrophe_test), &opts);
|
|
900
|
-
assert_contains(html, "id=\"whatsmarkdown\"", "MMD format removes curly apostrophe from ID");
|
|
901
|
-
apex_free_string(html);
|
|
902
|
-
|
|
903
|
-
/* Test --no-ids option */
|
|
904
|
-
opts.generate_header_ids = false;
|
|
905
|
-
html = apex_markdown_to_html("# Emoji Support", 16, &opts);
|
|
906
|
-
if (strstr(html, "id=") == NULL) {
|
|
907
|
-
test_result(true, "--no-ids disables ID generation");
|
|
908
|
-
} else {
|
|
909
|
-
test_result(false, "--no-ids still generates IDs");
|
|
910
|
-
}
|
|
911
|
-
apex_free_string(html);
|
|
912
|
-
|
|
913
|
-
/* Test diacritics handling */
|
|
914
|
-
opts.generate_header_ids = true;
|
|
915
|
-
opts.id_format = 0; /* GFM format */
|
|
916
|
-
const char *diacritics_test = "# Émoji Support\n## Test—Heading";
|
|
917
|
-
html = apex_markdown_to_html(diacritics_test, strlen(diacritics_test), &opts);
|
|
918
|
-
assert_contains(html, "id=\"emoji-support\"", "Diacritics converted (É→e)");
|
|
919
|
-
/* GFM: em dash should be removed (not converted) */
|
|
920
|
-
assert_contains(html, "id=\"testheading\"", "GFM removes em dash");
|
|
921
|
-
apex_free_string(html);
|
|
922
|
-
|
|
923
|
-
/* Test en dash in GFM */
|
|
924
|
-
const char *en_dash_test = "## Test–Heading";
|
|
925
|
-
html = apex_markdown_to_html(en_dash_test, strlen(en_dash_test), &opts);
|
|
926
|
-
assert_contains(html, "id=\"testheading\"", "GFM removes en dash");
|
|
927
|
-
apex_free_string(html);
|
|
928
|
-
|
|
929
|
-
/* Test GFM punctuation removal */
|
|
930
|
-
const char *gfm_punct_test = "# Hello, World!";
|
|
931
|
-
html = apex_markdown_to_html(gfm_punct_test, strlen(gfm_punct_test), &opts);
|
|
932
|
-
assert_contains(html, "id=\"hello-world\"", "GFM removes punctuation, spaces become dashes");
|
|
933
|
-
apex_free_string(html);
|
|
934
|
-
|
|
935
|
-
/* Test GFM multiple spaces collapse */
|
|
936
|
-
const char *gfm_spaces_test = "# Multiple Spaces";
|
|
937
|
-
html = apex_markdown_to_html(gfm_spaces_test, strlen(gfm_spaces_test), &opts);
|
|
938
|
-
assert_contains(html, "id=\"multiple-spaces\"", "GFM collapses multiple spaces to single dash");
|
|
939
|
-
apex_free_string(html);
|
|
940
|
-
|
|
941
|
-
/* Test special characters */
|
|
942
|
-
html = apex_markdown_to_html("# Hello, World!", 16, &opts);
|
|
943
|
-
assert_contains(html, "id=\"hello-world\"", "Comma and exclamation converted");
|
|
944
|
-
apex_free_string(html);
|
|
945
|
-
|
|
946
|
-
/* Test multiple spaces */
|
|
947
|
-
html = apex_markdown_to_html("# Multiple Spaces", 20, &opts);
|
|
948
|
-
assert_contains(html, "id=\"multiple-spaces\"", "Multiple spaces become single dash");
|
|
949
|
-
apex_free_string(html);
|
|
950
|
-
|
|
951
|
-
/* Test leading/trailing dashes trimmed */
|
|
952
|
-
html = apex_markdown_to_html("# -Leading Dash", 16, &opts);
|
|
953
|
-
assert_contains(html, "id=\"leading-dash\"", "Leading dash trimmed");
|
|
954
|
-
apex_free_string(html);
|
|
955
|
-
|
|
956
|
-
html = apex_markdown_to_html("# Trailing Dash-", 17, &opts);
|
|
957
|
-
assert_contains(html, "id=\"trailing-dash\"", "Trailing dash trimmed");
|
|
958
|
-
apex_free_string(html);
|
|
959
|
-
|
|
960
|
-
/* Test TOC uses same ID format */
|
|
961
|
-
opts.id_format = 0; /* GFM format */
|
|
962
|
-
const char *toc_doc = "# Main Title\n\n<!--TOC-->\n\n## Subtitle";
|
|
963
|
-
html = apex_markdown_to_html(toc_doc, strlen(toc_doc), &opts);
|
|
964
|
-
assert_contains(html, "id=\"main-title\"", "TOC header has GFM ID");
|
|
965
|
-
assert_contains(html, "href=\"#main-title\"", "TOC link uses GFM ID");
|
|
966
|
-
apex_free_string(html);
|
|
967
|
-
|
|
968
|
-
/* Test TOC with MMD format */
|
|
969
|
-
opts.id_format = 1; /* MMD format */
|
|
970
|
-
html = apex_markdown_to_html(toc_doc, strlen(toc_doc), &opts);
|
|
971
|
-
assert_contains(html, "id=\"maintitle\"", "TOC header has MMD ID");
|
|
972
|
-
assert_contains(html, "href=\"#maintitle\"", "TOC link uses MMD ID");
|
|
973
|
-
apex_free_string(html);
|
|
974
|
-
|
|
975
|
-
/* Test Kramdown format (spaces→dashes, removes em/en dashes and diacritics) */
|
|
976
|
-
opts.id_format = 2; /* Kramdown format */
|
|
977
|
-
html = apex_markdown_to_html("# header one", 12, &opts);
|
|
978
|
-
assert_contains(html, "id=\"header-one\"", "Kramdown: spaces become dashes");
|
|
979
|
-
apex_free_string(html);
|
|
980
|
-
|
|
981
|
-
const char *kramdown_em_dash_test = "# header—one";
|
|
982
|
-
html = apex_markdown_to_html(kramdown_em_dash_test, strlen(kramdown_em_dash_test), &opts);
|
|
983
|
-
assert_contains(html, "id=\"headerone\"", "Kramdown removes em dash");
|
|
984
|
-
apex_free_string(html);
|
|
985
|
-
|
|
986
|
-
const char *kramdown_en_dash_test = "# header–one";
|
|
987
|
-
html = apex_markdown_to_html(kramdown_en_dash_test, strlen(kramdown_en_dash_test), &opts);
|
|
988
|
-
assert_contains(html, "id=\"headerone\"", "Kramdown removes en dash");
|
|
989
|
-
apex_free_string(html);
|
|
990
|
-
|
|
991
|
-
const char *kramdown_diacritics_test = "# Émoji Support";
|
|
992
|
-
html = apex_markdown_to_html(kramdown_diacritics_test, strlen(kramdown_diacritics_test), &opts);
|
|
993
|
-
assert_contains(html, "id=\"moji-support\"", "Kramdown removes diacritics");
|
|
994
|
-
apex_free_string(html);
|
|
995
|
-
|
|
996
|
-
const char *kramdown_spaces_test = "# Multiple Spaces";
|
|
997
|
-
html = apex_markdown_to_html(kramdown_spaces_test, strlen(kramdown_spaces_test), &opts);
|
|
998
|
-
assert_contains(html, "id=\"multiple---spaces\"", "Kramdown: multiple spaces become multiple dashes");
|
|
999
|
-
apex_free_string(html);
|
|
1000
|
-
|
|
1001
|
-
const char *kramdown_punct_test = "# Hello, World!";
|
|
1002
|
-
html = apex_markdown_to_html(kramdown_punct_test, strlen(kramdown_punct_test), &opts);
|
|
1003
|
-
assert_contains(html, "id=\"hello-world\"", "Kramdown: punctuation becomes dash, trailing punctuation removed");
|
|
1004
|
-
apex_free_string(html);
|
|
1005
|
-
|
|
1006
|
-
const char *kramdown_leading_test = "# -Leading Dash";
|
|
1007
|
-
html = apex_markdown_to_html(kramdown_leading_test, strlen(kramdown_leading_test), &opts);
|
|
1008
|
-
assert_contains(html, "id=\"leading-dash\"", "Kramdown trims leading dash");
|
|
1009
|
-
apex_free_string(html);
|
|
1010
|
-
|
|
1011
|
-
const char *kramdown_trailing_test = "# Trailing Dash-";
|
|
1012
|
-
html = apex_markdown_to_html(kramdown_trailing_test, strlen(kramdown_trailing_test), &opts);
|
|
1013
|
-
assert_contains(html, "id=\"trailing-dash-\"", "Kramdown preserves trailing dash");
|
|
1014
|
-
apex_free_string(html);
|
|
1015
|
-
|
|
1016
|
-
const char *kramdown_punct_space_test = "# Test, Here";
|
|
1017
|
-
html = apex_markdown_to_html(kramdown_punct_space_test, strlen(kramdown_punct_space_test), &opts);
|
|
1018
|
-
assert_contains(html, "id=\"test-here\"", "Kramdown: punctuation→dash, following space skipped");
|
|
1019
|
-
apex_free_string(html);
|
|
1020
|
-
|
|
1021
|
-
/* Test empty header gets default ID */
|
|
1022
|
-
html = apex_markdown_to_html("#", 1, &opts);
|
|
1023
|
-
assert_contains(html, "id=\"header\"", "Empty header gets default ID");
|
|
1024
|
-
apex_free_string(html);
|
|
1025
|
-
|
|
1026
|
-
/* Test header with only special characters */
|
|
1027
|
-
html = apex_markdown_to_html("# !@#$%", 7, &opts);
|
|
1028
|
-
assert_contains(html, "id=\"header\"", "Special-only header gets default ID");
|
|
1029
|
-
apex_free_string(html);
|
|
1030
|
-
|
|
1031
|
-
/* Test --header-anchors option */
|
|
1032
|
-
opts.header_anchors = true;
|
|
1033
|
-
html = apex_markdown_to_html("# Test Header", 13, &opts);
|
|
1034
|
-
assert_contains(html, "<a href=\"#test-header\"", "Anchor tag has href attribute");
|
|
1035
|
-
assert_contains(html, "aria-hidden=\"true\"", "Anchor tag has aria-hidden");
|
|
1036
|
-
assert_contains(html, "class=\"anchor\"", "Anchor tag has anchor class");
|
|
1037
|
-
assert_contains(html, "id=\"test-header\"", "Anchor tag has id attribute");
|
|
1038
|
-
assert_contains(html, "<h1><a", "Anchor tag is inside header tag");
|
|
1039
|
-
assert_contains(html, "</a>Test Header</h1>", "Anchor tag comes before header text");
|
|
1040
|
-
apex_free_string(html);
|
|
1041
|
-
|
|
1042
|
-
/* Test anchor tags with multiple headers */
|
|
1043
|
-
const char *multi_header_test = "# Header One\n## Header Two";
|
|
1044
|
-
html = apex_markdown_to_html(multi_header_test, strlen(multi_header_test), &opts);
|
|
1045
|
-
assert_contains(html, "<h1><a href=\"#header-one\"", "First header has anchor");
|
|
1046
|
-
assert_contains(html, "<h2><a href=\"#header-two\"", "Second header has anchor");
|
|
1047
|
-
apex_free_string(html);
|
|
1048
|
-
|
|
1049
|
-
/* Test anchor tags with different ID formats */
|
|
1050
|
-
opts.id_format = 1; /* MMD format */
|
|
1051
|
-
html = apex_markdown_to_html("# Test Header", 13, &opts);
|
|
1052
|
-
assert_contains(html, "<a href=\"#testheader\"", "MMD format anchor tag");
|
|
1053
|
-
assert_contains(html, "id=\"testheader\"", "MMD format anchor ID");
|
|
1054
|
-
apex_free_string(html);
|
|
1055
|
-
|
|
1056
|
-
opts.id_format = 2; /* Kramdown format */
|
|
1057
|
-
html = apex_markdown_to_html("# Test Header", 13, &opts);
|
|
1058
|
-
assert_contains(html, "<a href=\"#test-header\"", "Kramdown format anchor tag");
|
|
1059
|
-
assert_contains(html, "id=\"test-header\"", "Kramdown format anchor ID");
|
|
1060
|
-
apex_free_string(html);
|
|
1061
|
-
|
|
1062
|
-
/* Test that header_anchors=false uses header IDs (default behavior) */
|
|
1063
|
-
opts.header_anchors = false;
|
|
1064
|
-
opts.id_format = 0; /* GFM format */
|
|
1065
|
-
html = apex_markdown_to_html("# Test Header", 13, &opts);
|
|
1066
|
-
assert_contains(html, "<h1 id=\"test-header\"", "Default mode uses header ID attribute");
|
|
1067
|
-
if (strstr(html, "<a href=") == NULL) {
|
|
1068
|
-
test_result(true, "Default mode does not use anchor tags");
|
|
1069
|
-
} else {
|
|
1070
|
-
test_result(false, "Default mode incorrectly uses anchor tags");
|
|
1071
|
-
}
|
|
1072
|
-
apex_free_string(html);
|
|
1073
|
-
|
|
1074
|
-
/* MMD heading [id] edge case: when [id] matches link ref but is last in heading
|
|
1075
|
-
* with other content, treat as heading ID not link */
|
|
1076
|
-
const char *mmd_id_conflict = "# Heading [mermaid]\n\n[mermaid]: https://example.com\n";
|
|
1077
|
-
html = apex_markdown_to_html(mmd_id_conflict, strlen(mmd_id_conflict), &opts);
|
|
1078
|
-
assert_contains(html, "id=\"mermaid\"", "MMD [id] at end of heading with link ref: treated as ID");
|
|
1079
|
-
assert_contains(html, "Heading mermaid", "MMD [id] at end: mermaid rendered as text not link");
|
|
1080
|
-
if (strstr(html, "<a href=\"https://example.com\">mermaid</a>") != NULL) {
|
|
1081
|
-
test_result(false, "MMD [id] at end should not render as link");
|
|
1082
|
-
} else {
|
|
1083
|
-
test_result(true, "MMD [id] at end: mermaid not rendered as link");
|
|
1084
|
-
}
|
|
1085
|
-
apex_free_string(html);
|
|
1086
|
-
|
|
1087
|
-
/* Entire heading is [id]: keep as link to avoid empty heading */
|
|
1088
|
-
const char *mmd_only_link = "# [mermaid]\n\n[mermaid]: https://example.com\n";
|
|
1089
|
-
html = apex_markdown_to_html(mmd_only_link, strlen(mmd_only_link), &opts);
|
|
1090
|
-
assert_contains(html, "<a href=\"https://example.com\"", "Heading only [id] with link ref: remains link");
|
|
1091
|
-
assert_contains(html, ">mermaid</a>", "Heading only [id]: mermaid renders as link text");
|
|
1092
|
-
apex_free_string(html);
|
|
1093
|
-
|
|
1094
|
-
/* [id] in middle of heading: remains link */
|
|
1095
|
-
const char *mmd_link_middle = "# Check out [mermaid] for diagrams\n\n[mermaid]: https://example.com\n";
|
|
1096
|
-
html = apex_markdown_to_html(mmd_link_middle, strlen(mmd_link_middle), &opts);
|
|
1097
|
-
assert_contains(html, "<a href=\"https://example.com\"", "MMD [id] in middle: remains link");
|
|
1098
|
-
apex_free_string(html);
|
|
1099
|
-
|
|
1100
|
-
bool had_failures = suite_end(suite_failures);
|
|
1101
|
-
print_suite_title("Header ID Generation Tests", had_failures, false);
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
/**
|
|
1105
|
-
* Test superscript, subscript, underline, strikethrough, and highlight
|
|
1106
|
-
*/
|
|
1107
|
-
|
|
1108
|
-
void test_indices(void) {
|
|
1109
|
-
int suite_failures = suite_start();
|
|
1110
|
-
print_suite_title("Index Tests", false, true);
|
|
1111
|
-
|
|
1112
|
-
apex_options opts = apex_options_default();
|
|
1113
|
-
opts.mode = APEX_MODE_UNIFIED;
|
|
1114
|
-
opts.enable_indices = true;
|
|
1115
|
-
opts.enable_mmark_index_syntax = true;
|
|
1116
|
-
opts.enable_textindex_syntax = true;
|
|
1117
|
-
opts.enable_leanpub_index_syntax = true;
|
|
1118
|
-
opts.group_index_by_letter = true;
|
|
1119
|
-
|
|
1120
|
-
char *html;
|
|
1121
|
-
|
|
1122
|
-
/* Test basic mmark index syntax */
|
|
1123
|
-
const char *mmark_basic = "This is about protocols (!Protocol).";
|
|
1124
|
-
html = apex_markdown_to_html(mmark_basic, strlen(mmark_basic), &opts);
|
|
1125
|
-
assert_contains(html, "class=\"index\"", "mmark index generates index marker");
|
|
1126
|
-
assert_contains(html, "idxref-", "mmark index generates anchor ID");
|
|
1127
|
-
assert_contains(html, "Protocol", "mmark index preserves term");
|
|
1128
|
-
apex_free_string(html);
|
|
1129
|
-
|
|
1130
|
-
/* Test mmark index with subitem */
|
|
1131
|
-
const char *mmark_subitem = "HTTP/1.1 (!HTTP, HTTP/1.1) is a protocol.";
|
|
1132
|
-
html = apex_markdown_to_html(mmark_subitem, strlen(mmark_subitem), &opts);
|
|
1133
|
-
assert_contains(html, "class=\"index\"", "mmark subitem generates index marker");
|
|
1134
|
-
assert_contains(html, "HTTP", "mmark subitem includes main term");
|
|
1135
|
-
apex_free_string(html);
|
|
1136
|
-
|
|
1137
|
-
/* Test mmark primary index entry */
|
|
1138
|
-
const char *mmark_primary = "This is a primary topic (!!Primary Topic, Sub Topic).";
|
|
1139
|
-
html = apex_markdown_to_html(mmark_primary, strlen(mmark_primary), &opts);
|
|
1140
|
-
assert_contains(html, "class=\"index\"", "mmark primary entry generates index marker");
|
|
1141
|
-
assert_contains(html, "Primary Topic", "mmark primary entry includes term");
|
|
1142
|
-
apex_free_string(html);
|
|
1143
|
-
|
|
1144
|
-
/* Test multiple mmark index entries */
|
|
1145
|
-
const char *mmark_multiple = "Protocols (!Protocol) and implementations (!Implementation) are important.";
|
|
1146
|
-
html = apex_markdown_to_html(mmark_multiple, strlen(mmark_multiple), &opts);
|
|
1147
|
-
assert_contains(html, "Protocol", "Multiple mmark entries include first term");
|
|
1148
|
-
assert_contains(html, "Implementation", "Multiple mmark entries include second term");
|
|
1149
|
-
apex_free_string(html);
|
|
1150
|
-
|
|
1151
|
-
/* Test TextIndex basic syntax */
|
|
1152
|
-
const char *textindex_basic = "This is about firmware{^}.";
|
|
1153
|
-
html = apex_markdown_to_html(textindex_basic, strlen(textindex_basic), &opts);
|
|
1154
|
-
assert_contains(html, "class=\"index\"", "TextIndex generates index marker");
|
|
1155
|
-
assert_contains(html, "idxref-", "TextIndex generates anchor ID");
|
|
1156
|
-
apex_free_string(html);
|
|
1157
|
-
|
|
1158
|
-
/* Test TextIndex with explicit term */
|
|
1159
|
-
const char *textindex_explicit = "This uses [key combinations]{^}.";
|
|
1160
|
-
html = apex_markdown_to_html(textindex_explicit, strlen(textindex_explicit), &opts);
|
|
1161
|
-
assert_contains(html, "class=\"index\"", "TextIndex explicit term generates marker");
|
|
1162
|
-
assert_contains(html, "key combinations", "TextIndex explicit term keeps visible text");
|
|
1163
|
-
assert_not_contains(html, "[key combinations]", "TextIndex explicit term strips square brackets");
|
|
1164
|
-
assert_contains(html, "key combinations <a class=\"index-return\"", "TextIndex index lists explicit term");
|
|
1165
|
-
apex_free_string(html);
|
|
1166
|
-
|
|
1167
|
-
/* Bare {^} indexes only the preceding word; [phrase]{^} is for multi-word terms */
|
|
1168
|
-
const char *textindex_word =
|
|
1169
|
-
"Most mechanical keyboard firmware{^} supports the use of [key combinations]{^}.";
|
|
1170
|
-
html = apex_markdown_to_html(textindex_word, strlen(textindex_word), &opts);
|
|
1171
|
-
assert_contains(html, "firmware<span class=\"index\"", "TextIndex bare {^} leaves preceding word");
|
|
1172
|
-
assert_not_contains(html, "Most mechanical keyboard firmware <a", "TextIndex bare {^} indexes word only, not whole phrase");
|
|
1173
|
-
assert_contains(html, "firmware <a class=\"index-return\"", "TextIndex bare {^} index entry is firmware");
|
|
1174
|
-
assert_not_contains(html, "[key combinations]", "TextIndex phrase form strips brackets in sentence");
|
|
1175
|
-
assert_contains(html, "of key combinations<span class=\"index\"", "TextIndex phrase form keeps unbracketed text");
|
|
1176
|
-
apex_free_string(html);
|
|
1177
|
-
|
|
1178
|
-
/* Quoted heading override / standalone mark (TextIndex Fig. 2) */
|
|
1179
|
-
const char *textindex_quoted =
|
|
1180
|
-
"This is a standalone mark: {^\"foo bar\"} and this one has an overridden [heading]{^\"foo baz\"}.";
|
|
1181
|
-
html = apex_markdown_to_html(textindex_quoted, strlen(textindex_quoted), &opts);
|
|
1182
|
-
assert_not_contains(html, "{^", "TextIndex quoted marks are consumed (not left for superscript)");
|
|
1183
|
-
assert_not_contains(html, "{<sup>", "TextIndex quoted marks are not turned into superscript");
|
|
1184
|
-
assert_contains(html, "standalone mark: <span class=\"index\"", "TextIndex standalone quoted mark leaves only index span");
|
|
1185
|
-
assert_contains(html, "overridden heading<span class=\"index\"", "TextIndex override keeps visible heading without brackets");
|
|
1186
|
-
assert_not_contains(html, "[heading]", "TextIndex override strips square brackets");
|
|
1187
|
-
assert_contains(html, "foo bar <a class=\"index-return\"", "TextIndex standalone quoted mark indexes foo bar");
|
|
1188
|
-
assert_contains(html, "foo baz <a class=\"index-return\"", "TextIndex override indexes quoted term not visible heading");
|
|
1189
|
-
assert_not_contains(html, "heading <a class=\"index-return\"", "TextIndex override does not index visible heading text");
|
|
1190
|
-
apex_free_string(html);
|
|
1191
|
-
|
|
1192
|
-
/* TextIndex Fig. 4-5: underscore emphasis and * / ** wildcards */
|
|
1193
|
-
const char *textindex_emph =
|
|
1194
|
-
"This entry will be _emphasised_{^} in the index. "
|
|
1195
|
-
"This expanded entry won't be _emphasised_{^\"* (nope)\"}.";
|
|
1196
|
-
html = apex_markdown_to_html(textindex_emph, strlen(textindex_emph), &opts);
|
|
1197
|
-
assert_contains(html, "<em>emphasised</em><span class=\"index\"",
|
|
1198
|
-
"TextIndex emphasised heading keeps emphasis in body");
|
|
1199
|
-
assert_contains(html, "<em>emphasised</em> <a class=\"index-return\"",
|
|
1200
|
-
"TextIndex emphasised heading is emphasised in index");
|
|
1201
|
-
assert_not_contains(html, "_emphasised_",
|
|
1202
|
-
"TextIndex emphasised heading does not keep raw underscores in index");
|
|
1203
|
-
assert_contains(html, "emphasised (nope) <a class=\"index-return\"",
|
|
1204
|
-
"TextIndex * wildcard expands preceding without emphasis");
|
|
1205
|
-
assert_not_contains(html, "* (nope)",
|
|
1206
|
-
"TextIndex * wildcard is not left literal in index");
|
|
1207
|
-
apex_free_string(html);
|
|
1208
|
-
|
|
1209
|
-
const char *textindex_emph_strip = "This entry won't be _emphasised_{^*}.";
|
|
1210
|
-
html = apex_markdown_to_html(textindex_emph_strip, strlen(textindex_emph_strip), &opts);
|
|
1211
|
-
assert_contains(html, "emphasised <a class=\"index-return\"",
|
|
1212
|
-
"TextIndex {^*} strips emphasis via wildcard");
|
|
1213
|
-
assert_not_contains(html, "<em>emphasised</em> <a class=\"index-return\"",
|
|
1214
|
-
"TextIndex {^*} index entry is not emphasised");
|
|
1215
|
-
assert_not_contains(html, "_emphasised_",
|
|
1216
|
-
"TextIndex {^*} does not keep raw underscores in index");
|
|
1217
|
-
apex_free_string(html);
|
|
1218
|
-
|
|
1219
|
-
const char *textindex_emph_lower = "Introducing _Widget_{^\"**\"}.";
|
|
1220
|
-
html = apex_markdown_to_html(textindex_emph_lower, strlen(textindex_emph_lower), &opts);
|
|
1221
|
-
assert_contains(html, "widget <a class=\"index-return\"",
|
|
1222
|
-
"TextIndex ** wildcard lowercases stripped preceding");
|
|
1223
|
-
apex_free_string(html);
|
|
1224
|
-
|
|
1225
|
-
/* TextIndex Fig. 6: prefix wildcards — same heading merges locators */
|
|
1226
|
-
const char *textindex_prefix =
|
|
1227
|
-
"Sir Winston Leonard Spencer Churchill{^\"*, Sir Winston Leonard Spencer\"} "
|
|
1228
|
-
"was Prime Minister from 1940 to 1945.\n\n"
|
|
1229
|
-
"Churchill{^*^} was born on 30 November 1874.";
|
|
1230
|
-
html = apex_markdown_to_html(textindex_prefix, strlen(textindex_prefix), &opts);
|
|
1231
|
-
assert_not_contains(html, "Churchill^",
|
|
1232
|
-
"TextIndex *^ is not expanded as a plain * wildcard");
|
|
1233
|
-
assert_contains(html, "href=\"#idxref-0\"", "TextIndex merged entry includes first locator");
|
|
1234
|
-
assert_contains(html, "href=\"#idxref-1\"", "TextIndex merged entry includes second locator");
|
|
1235
|
-
{
|
|
1236
|
-
int heading_hits = 0;
|
|
1237
|
-
for (const char *p = html; (p = strstr(p, "Churchill, Sir Winston Leonard Spencer <a class=\"index-return\"")) != NULL; p++) {
|
|
1238
|
-
heading_hits++;
|
|
1239
|
-
}
|
|
1240
|
-
test_result(heading_hits == 1, "TextIndex *^ merges into one index entry with both locators");
|
|
1241
|
-
}
|
|
1242
|
-
apex_free_string(html);
|
|
1243
|
-
|
|
1244
|
-
/* Duplicate mmark marks also merge locators */
|
|
1245
|
-
const char *mmark_dup = "Alpha (!Alpha) here.\n\nMore about Alpha (!Alpha) there.";
|
|
1246
|
-
html = apex_markdown_to_html(mmark_dup, strlen(mmark_dup), &opts);
|
|
1247
|
-
assert_contains(html, "href=\"#idxref-0\"", "mmark merged entry has first locator");
|
|
1248
|
-
assert_contains(html, "href=\"#idxref-1\"", "mmark merged entry has second locator");
|
|
1249
|
-
{
|
|
1250
|
-
int hits = 0;
|
|
1251
|
-
for (const char *p = html; (p = strstr(p, "Alpha <a class=\"index-return\"")) != NULL; p++) {
|
|
1252
|
-
hits++;
|
|
1253
|
-
}
|
|
1254
|
-
test_result(hits == 1, "mmark duplicate marks merge into one entry");
|
|
1255
|
-
}
|
|
1256
|
-
apex_free_string(html);
|
|
1257
|
-
|
|
1258
|
-
/* TextIndex Fig. 11-12: see / see-also cross-references */
|
|
1259
|
-
const char *textindex_see =
|
|
1260
|
-
"This function can be [dangerous]{^\"destructive operation\"|safety>\"of functions\"}.";
|
|
1261
|
-
html = apex_markdown_to_html(textindex_see, strlen(textindex_see), &opts);
|
|
1262
|
-
assert_contains(html, "be dangerous", "TextIndex see-type keeps visible bracketed text");
|
|
1263
|
-
assert_not_contains(html, "[dangerous]", "TextIndex see-type strips brackets");
|
|
1264
|
-
assert_contains(html, "destructive operation", "TextIndex see-type indexes overridden heading");
|
|
1265
|
-
assert_contains(html, "<em>See</em>", "TextIndex see-type renders See in italics");
|
|
1266
|
-
assert_contains(html, "safety: of functions", "TextIndex see-type shows hierarchical path with colon");
|
|
1267
|
-
assert_not_contains(html, "href=\"#idxref-", "TextIndex see-type does not record a locator");
|
|
1268
|
-
apex_free_string(html);
|
|
1269
|
-
|
|
1270
|
-
const char *textindex_see_also =
|
|
1271
|
-
"Even typing on a keyboard can be surprisingly risky{^\"risk\"|ergonomics;+safety}.";
|
|
1272
|
-
html = apex_markdown_to_html(textindex_see_also, strlen(textindex_see_also), &opts);
|
|
1273
|
-
assert_contains(html, "<em>See</em>", "TextIndex mixed xref renders See");
|
|
1274
|
-
assert_contains(html, "ergonomics", "TextIndex mixed xref includes see target");
|
|
1275
|
-
assert_contains(html, "<em>See also</em>", "TextIndex mixed xref renders See also");
|
|
1276
|
-
assert_contains(html, "safety", "TextIndex mixed xref includes also target");
|
|
1277
|
-
assert_not_contains(html, "href=\"#idxref-", "TextIndex see-type in mark suppresses locator");
|
|
1278
|
-
apex_free_string(html);
|
|
1279
|
-
|
|
1280
|
-
const char *textindex_also_only =
|
|
1281
|
-
"A mention of risk{^\"risk\"|+safety} alone.";
|
|
1282
|
-
html = apex_markdown_to_html(textindex_also_only, strlen(textindex_also_only), &opts);
|
|
1283
|
-
assert_contains(html, "<em>See also</em>", "TextIndex also-only renders See also");
|
|
1284
|
-
assert_contains(html, "href=\"#idxref-", "TextIndex also-only keeps locator");
|
|
1285
|
-
assert_not_contains(html, "<em>See</em> safety", "TextIndex also-only is not a see-type");
|
|
1286
|
-
apex_free_string(html);
|
|
1287
|
-
|
|
1288
|
-
/* TextIndex Fig. 13: aliases */
|
|
1289
|
-
const char *textindex_alias =
|
|
1290
|
-
"The various [operating systems]{^\"Apple (company)\">\"OS platforms\"#apple} "
|
|
1291
|
-
"on Apple devices share a common heritage.\n\n"
|
|
1292
|
-
"The majority of Apple devices are iPhones{^\"iPhone\"|+#apple}, by a large margin.";
|
|
1293
|
-
html = apex_markdown_to_html(textindex_alias, strlen(textindex_alias), &opts);
|
|
1294
|
-
assert_contains(html, "operating systems<span class=\"index\"",
|
|
1295
|
-
"TextIndex alias definition keeps visible text");
|
|
1296
|
-
assert_contains(html, "Apple (company)", "TextIndex alias definition indexes parent heading");
|
|
1297
|
-
assert_contains(html, "OS platforms", "TextIndex alias definition indexes nested heading");
|
|
1298
|
-
assert_contains(html, "iPhone", "TextIndex alias use indexes iPhone");
|
|
1299
|
-
assert_contains(html, "<em>See also</em>", "TextIndex alias use expands as see-also");
|
|
1300
|
-
assert_contains(html, "Apple (company): OS platforms",
|
|
1301
|
-
"TextIndex #apple expands to full path in see-also");
|
|
1302
|
-
assert_not_contains(html, "#apple", "TextIndex alias name is not left literal");
|
|
1303
|
-
apex_free_string(html);
|
|
1304
|
-
|
|
1305
|
-
/* TextIndex Fig. 14: unreferenced aliases */
|
|
1306
|
-
const char *textindex_unref_alias =
|
|
1307
|
-
"{^\"indeterminacy principle (Heisenberg, Werner Karl)\"##q-uncert} "
|
|
1308
|
-
"A key component of the Enterprise's transporter system is the "
|
|
1309
|
-
"Heisenberg compensator{^|#q-uncert}.";
|
|
1310
|
-
html = apex_markdown_to_html(textindex_unref_alias, strlen(textindex_unref_alias), &opts);
|
|
1311
|
-
assert_contains(html, "compensator", "TextIndex unref alias use keeps preceding word");
|
|
1312
|
-
assert_contains(html, "<em>See</em>", "TextIndex unref alias used as see-reference");
|
|
1313
|
-
assert_contains(html, "indeterminacy principle (Heisenberg, Werner Karl)",
|
|
1314
|
-
"TextIndex ## alias expands in see target");
|
|
1315
|
-
assert_not_contains(html, "#q-uncert", "TextIndex ## alias name is not left literal");
|
|
1316
|
-
assert_not_contains(html, "indeterminacy principle (Heisenberg, Werner Karl) <a class=\"index-return\"",
|
|
1317
|
-
"TextIndex ## alias does not create a locator for the definition");
|
|
1318
|
-
apex_free_string(html);
|
|
1319
|
-
|
|
1320
|
-
/* TextIndex Fig. 16: disable/enable processing with {^-} / {^+} */
|
|
1321
|
-
const char *textindex_toggle =
|
|
1322
|
-
"{^-} This index mark won't be processed: foo{^} {^+} after{^}.";
|
|
1323
|
-
html = apex_markdown_to_html(textindex_toggle, strlen(textindex_toggle), &opts);
|
|
1324
|
-
assert_contains(html, "foo{^}", "TextIndex {^-} leaves marks untouched while disabled");
|
|
1325
|
-
assert_contains(html, "after<span class=\"index\"", "TextIndex {^+} re-enables processing");
|
|
1326
|
-
assert_contains(html, "after <a class=\"index-return\"", "TextIndex mark after {^+} is indexed");
|
|
1327
|
-
assert_not_contains(html, "foo <a class=\"index-return\"", "TextIndex mark inside {^-} region is not indexed");
|
|
1328
|
-
assert_not_contains(html, "{^-}", "TextIndex {^-} toggle mark is consumed when it disables");
|
|
1329
|
-
assert_not_contains(html, "{^+}", "TextIndex {^+} toggle mark is consumed when it enables");
|
|
1330
|
-
assert_not_contains(html, "> - <a class=\"index-return\"", "TextIndex {^-} is not indexed as a heading");
|
|
1331
|
-
assert_not_contains(html, "> + <a class=\"index-return\"", "TextIndex {^+} is not indexed as a heading");
|
|
1332
|
-
apex_free_string(html);
|
|
1333
|
-
|
|
1334
|
-
const char *textindex_toggle_redundant =
|
|
1335
|
-
"{^-} hidden{^} {^-} still{^} {^+} shown{^} {^+} trailing.";
|
|
1336
|
-
html = apex_markdown_to_html(textindex_toggle_redundant, strlen(textindex_toggle_redundant), &opts);
|
|
1337
|
-
assert_contains(html, "hidden{^}", "TextIndex redundant region keeps first disabled mark literal");
|
|
1338
|
-
assert_contains(html, "{^-}", "TextIndex redundant {^-} stays when already disabled");
|
|
1339
|
-
assert_contains(html, "still{^}", "TextIndex marks stay literal until {^+}");
|
|
1340
|
-
assert_contains(html, "shown<span class=\"index\"", "TextIndex processing resumes after effective {^+}");
|
|
1341
|
-
assert_contains(html, "{^+}", "TextIndex redundant {^+} stays when already enabled");
|
|
1342
|
-
apex_free_string(html);
|
|
1343
|
-
|
|
1344
|
-
/* Test Leanpub index syntax */
|
|
1345
|
-
const char *leanpub_basic = "Call me Ishmael{i: Ishmael}.";
|
|
1346
|
-
html = apex_markdown_to_html(leanpub_basic, strlen(leanpub_basic), &opts);
|
|
1347
|
-
assert_contains(html, "class=\"index\"", "Leanpub index generates index marker");
|
|
1348
|
-
assert_contains(html, "Ishmael", "Leanpub index preserves term");
|
|
1349
|
-
apex_free_string(html);
|
|
1350
|
-
|
|
1351
|
-
/* Test Leanpub hierarchical index */
|
|
1352
|
-
apex_options opts_gfm = apex_options_for_mode(APEX_MODE_GFM);
|
|
1353
|
-
opts_gfm.enable_indices = true;
|
|
1354
|
-
opts_gfm.enable_leanpub_index_syntax = true;
|
|
1355
|
-
const char *leanpub_hier = "Niagara{i: \"Niagara!cataract\"} of sand.";
|
|
1356
|
-
html = apex_markdown_to_html(leanpub_hier, strlen(leanpub_hier), &opts_gfm);
|
|
1357
|
-
assert_contains(html, "Niagara", "Leanpub hierarchical includes main term");
|
|
1358
|
-
assert_contains(html, "cataract", "Leanpub hierarchical includes subitem");
|
|
1359
|
-
apex_free_string(html);
|
|
1360
|
-
|
|
1361
|
-
/* Test index generation at end of document */
|
|
1362
|
-
const char *with_index = "This is about protocols (!Protocol).\n\nAnd implementations (!Implementation).";
|
|
1363
|
-
html = apex_markdown_to_html(with_index, strlen(with_index), &opts);
|
|
1364
|
-
assert_contains(html, "id=\"index-section\"", "Index section generated");
|
|
1365
|
-
assert_contains(html, "class=\"index\"", "Index div generated");
|
|
1366
|
-
assert_contains(html, "Protocol", "Index includes first entry");
|
|
1367
|
-
assert_contains(html, "Implementation", "Index includes second entry");
|
|
1368
|
-
apex_free_string(html);
|
|
1369
|
-
|
|
1370
|
-
/* Test index with alphabetical grouping */
|
|
1371
|
-
const char *grouped_index = "Apple (!Apple).\n\nBanana (!Banana).\n\nCherry (!Cherry).";
|
|
1372
|
-
html = apex_markdown_to_html(grouped_index, strlen(grouped_index), &opts);
|
|
1373
|
-
assert_contains(html, "<dt>A</dt>", "Index groups by first letter (A)");
|
|
1374
|
-
assert_contains(html, "<dt>B</dt>", "Index groups by first letter (B)");
|
|
1375
|
-
assert_contains(html, "<dt>C</dt>", "Index groups by first letter (C)");
|
|
1376
|
-
apex_free_string(html);
|
|
1377
|
-
|
|
1378
|
-
/* Test index insertion at <!--INDEX--> marker */
|
|
1379
|
-
const char *index_marker = "This is about protocols (!Protocol).\n\n<!--INDEX-->\n\nMore content.";
|
|
1380
|
-
html = apex_markdown_to_html(index_marker, strlen(index_marker), &opts);
|
|
1381
|
-
assert_contains(html, "id=\"index-section\"", "Index inserted at marker");
|
|
1382
|
-
assert_not_contains(html, "<!--INDEX-->", "Index marker replaced");
|
|
1383
|
-
/* Index should appear before "More content" */
|
|
1384
|
-
const char *more_pos = strstr(html, "More content");
|
|
1385
|
-
const char *index_pos = strstr(html, "id=\"index-section\"");
|
|
1386
|
-
if (more_pos && index_pos) {
|
|
1387
|
-
assert_contains(html, "id=\"index-section\"", "Index appears before marker content");
|
|
1388
|
-
}
|
|
1389
|
-
apex_free_string(html);
|
|
1390
|
-
|
|
1391
|
-
/* Test index with subitems */
|
|
1392
|
-
const char *with_subitems = "Symmetric encryption (!Encryption, Symmetric).\n\nAsymmetric encryption (!Encryption, Asymmetric).";
|
|
1393
|
-
html = apex_markdown_to_html(with_subitems, strlen(with_subitems), &opts);
|
|
1394
|
-
assert_contains(html, "Encryption", "Index includes main term");
|
|
1395
|
-
assert_contains(html, "Symmetric", "Index includes first subitem");
|
|
1396
|
-
assert_contains(html, "Asymmetric", "Index includes second subitem");
|
|
1397
|
-
apex_free_string(html);
|
|
1398
|
-
|
|
1399
|
-
/* Test suppress_index option */
|
|
1400
|
-
apex_options opts_suppress = apex_options_default();
|
|
1401
|
-
opts_suppress.mode = APEX_MODE_UNIFIED;
|
|
1402
|
-
opts_suppress.enable_indices = true;
|
|
1403
|
-
opts_suppress.enable_mmark_index_syntax = true;
|
|
1404
|
-
opts_suppress.suppress_index = true;
|
|
1405
|
-
const char *suppress_test = "This is about protocols (!Protocol).";
|
|
1406
|
-
html = apex_markdown_to_html(suppress_test, strlen(suppress_test), &opts_suppress);
|
|
1407
|
-
assert_contains(html, "class=\"index\"", "Index markers still generated when suppressed");
|
|
1408
|
-
assert_not_contains(html, "id=\"index-section\"", "Index section not generated when suppressed");
|
|
1409
|
-
apex_free_string(html);
|
|
1410
|
-
|
|
1411
|
-
/* Test indices disabled */
|
|
1412
|
-
apex_options opts_disabled = apex_options_default();
|
|
1413
|
-
opts_disabled.mode = APEX_MODE_UNIFIED;
|
|
1414
|
-
opts_disabled.enable_indices = false;
|
|
1415
|
-
const char *disabled_test = "This is about protocols (!Protocol).";
|
|
1416
|
-
html = apex_markdown_to_html(disabled_test, strlen(disabled_test), &opts_disabled);
|
|
1417
|
-
assert_not_contains(html, "class=\"index\"", "Index markers not generated when disabled");
|
|
1418
|
-
assert_not_contains(html, "idxref-", "Index anchors not generated when disabled");
|
|
1419
|
-
apex_free_string(html);
|
|
1420
|
-
|
|
1421
|
-
/* Test mmark syntax only mode */
|
|
1422
|
-
apex_options opts_mmark_only = apex_options_default();
|
|
1423
|
-
opts_mmark_only.mode = APEX_MODE_UNIFIED;
|
|
1424
|
-
opts_mmark_only.enable_indices = true;
|
|
1425
|
-
opts_mmark_only.enable_mmark_index_syntax = true;
|
|
1426
|
-
opts_mmark_only.enable_textindex_syntax = false;
|
|
1427
|
-
const char *mmark_only_test = "Protocols (!Protocol) and firmware{^}.";
|
|
1428
|
-
html = apex_markdown_to_html(mmark_only_test, strlen(mmark_only_test), &opts_mmark_only);
|
|
1429
|
-
assert_contains(html, "class=\"index\"", "mmark syntax processed when enabled");
|
|
1430
|
-
/* TextIndex {^} should not be processed, so {^} should appear as plain text or be converted to superscript */
|
|
1431
|
-
assert_not_contains(html, "firmware<span class=\"index\"", "TextIndex syntax not processed when disabled");
|
|
1432
|
-
apex_free_string(html);
|
|
1433
|
-
|
|
1434
|
-
/* Test TextIndex syntax only mode */
|
|
1435
|
-
apex_options opts_textindex_only = apex_options_default();
|
|
1436
|
-
opts_textindex_only.mode = APEX_MODE_UNIFIED;
|
|
1437
|
-
opts_textindex_only.enable_indices = true;
|
|
1438
|
-
opts_textindex_only.enable_mmark_index_syntax = false;
|
|
1439
|
-
opts_textindex_only.enable_textindex_syntax = true;
|
|
1440
|
-
const char *textindex_only_test = "Protocols (!Protocol) and firmware{^}.";
|
|
1441
|
-
html = apex_markdown_to_html(textindex_only_test, strlen(textindex_only_test), &opts_textindex_only);
|
|
1442
|
-
/* mmark syntax should not be processed, so (!Protocol) should appear as plain text */
|
|
1443
|
-
assert_contains(html, "(!Protocol)", "mmark syntax not processed when disabled");
|
|
1444
|
-
assert_contains(html, "class=\"index\"", "TextIndex syntax processed when enabled");
|
|
1445
|
-
/* Check that mmark syntax wasn't processed by verifying no index entry for Protocol in index section */
|
|
1446
|
-
const char *protocol_in_index = strstr(html, "id=\"index-section\"");
|
|
1447
|
-
if (protocol_in_index) {
|
|
1448
|
-
/* Look for "Protocol" in the index section - it shouldn't be there if mmark wasn't processed */
|
|
1449
|
-
const char *protocol_entry = strstr(protocol_in_index, ">Protocol<");
|
|
1450
|
-
if (protocol_entry == NULL) {
|
|
1451
|
-
test_result(true, "mmark syntax not processed when disabled");
|
|
1452
|
-
} else {
|
|
1453
|
-
test_result(false, "mmark syntax not processed when disabled");
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
apex_free_string(html);
|
|
1457
|
-
|
|
1458
|
-
/* Test index without grouping */
|
|
1459
|
-
apex_options opts_no_group = apex_options_default();
|
|
1460
|
-
opts_no_group.mode = APEX_MODE_UNIFIED;
|
|
1461
|
-
opts_no_group.enable_indices = true;
|
|
1462
|
-
opts_no_group.enable_mmark_index_syntax = true;
|
|
1463
|
-
opts_no_group.group_index_by_letter = false;
|
|
1464
|
-
const char *no_group_test = "Apple (!Apple).\n\nBanana (!Banana).";
|
|
1465
|
-
html = apex_markdown_to_html(no_group_test, strlen(no_group_test), &opts_no_group);
|
|
1466
|
-
assert_contains(html, "id=\"index-section\"", "Index generated without grouping");
|
|
1467
|
-
assert_not_contains(html, "<dt>A</dt>", "Index not grouped by letter when disabled");
|
|
1468
|
-
assert_contains(html, "<ul>", "Index uses simple list when not grouped");
|
|
1469
|
-
apex_free_string(html);
|
|
1470
|
-
|
|
1471
|
-
/* Test index in MultiMarkdown mode (now requires explicit --indices flag) */
|
|
1472
|
-
apex_options opts_mmd = apex_options_for_mode(APEX_MODE_MULTIMARKDOWN);
|
|
1473
|
-
opts_mmd.enable_indices = true;
|
|
1474
|
-
opts_mmd.enable_mmark_index_syntax = true;
|
|
1475
|
-
const char *mmd_test = "This is about protocols (!Protocol).";
|
|
1476
|
-
html = apex_markdown_to_html(mmd_test, strlen(mmd_test), &opts_mmd);
|
|
1477
|
-
assert_contains(html, "class=\"index\"", "Indices enabled in MultiMarkdown mode");
|
|
1478
|
-
assert_contains(html, "Protocol", "mmark syntax works in MultiMarkdown mode");
|
|
1479
|
-
apex_free_string(html);
|
|
1480
|
-
|
|
1481
|
-
/* Test that index entries link back to document */
|
|
1482
|
-
const char *link_test = "This is about protocols (!Protocol).";
|
|
1483
|
-
html = apex_markdown_to_html(link_test, strlen(link_test), &opts);
|
|
1484
|
-
assert_contains(html, "index-return", "Index entries have return links");
|
|
1485
|
-
assert_contains(html, "href=\"#idxref-", "Index entries link to anchors");
|
|
1486
|
-
apex_free_string(html);
|
|
1487
|
-
|
|
1488
|
-
/* Concordance files: auto-mark terms from TSV before TextIndex processing */
|
|
1489
|
-
{
|
|
1490
|
-
apex_options opts_conc = apex_options_default();
|
|
1491
|
-
opts_conc.mode = APEX_MODE_UNIFIED;
|
|
1492
|
-
opts_conc.enable_indices = true;
|
|
1493
|
-
opts_conc.enable_textindex_syntax = true;
|
|
1494
|
-
opts_conc.base_directory = "tests";
|
|
1495
|
-
const char *conc_files[] = {"test_concordance.tsv", NULL};
|
|
1496
|
-
opts_conc.concordance_files = (char **)conc_files;
|
|
1497
|
-
|
|
1498
|
-
const char *conc_doc =
|
|
1499
|
-
"A widget and another Widget appear. The MacBook is nice. Configuration helps.";
|
|
1500
|
-
html = apex_markdown_to_html(conc_doc, strlen(conc_doc), &opts_conc);
|
|
1501
|
-
assert_contains(html, "gadget <a class=\"index-return\"",
|
|
1502
|
-
"Concordance maps widget to gadget heading");
|
|
1503
|
-
assert_contains(html, "Apple laptop <a class=\"index-return\"",
|
|
1504
|
-
"Concordance case-sensitive MacBook uses quoted heading");
|
|
1505
|
-
assert_contains(html, "See", "Concordance see-ref from column 2 is rendered");
|
|
1506
|
-
assert_contains(html, "firmware", "Concordance see target firmware appears");
|
|
1507
|
-
/* Existing marks must not be rematched */
|
|
1508
|
-
const char *conc_existing = "Already marked widget{^custom}.";
|
|
1509
|
-
apex_free_string(html);
|
|
1510
|
-
html = apex_markdown_to_html(conc_existing, strlen(conc_existing), &opts_conc);
|
|
1511
|
-
assert_contains(html, "custom <a class=\"index-return\"",
|
|
1512
|
-
"Concordance skips text already inside an index mark");
|
|
1513
|
-
assert_not_contains(html, "gadget <a class=\"index-return\"",
|
|
1514
|
-
"Concordance does not rematch marked widget as gadget");
|
|
1515
|
-
apex_free_string(html);
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
bool had_failures = suite_end(suite_failures);
|
|
1519
|
-
print_suite_title("Index Tests", had_failures, false);
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
/**
|
|
1523
|
-
* Test citation and bibliography features
|
|
1524
|
-
*/
|
|
1525
|
-
|
|
1526
|
-
void test_citations(void) {
|
|
1527
|
-
int suite_failures = suite_start();
|
|
1528
|
-
print_suite_title("Citation and Bibliography Tests", false, true);
|
|
1529
|
-
|
|
1530
|
-
apex_options opts = apex_options_default();
|
|
1531
|
-
opts.mode = APEX_MODE_UNIFIED;
|
|
1532
|
-
opts.enable_citations = true;
|
|
1533
|
-
opts.base_directory = "tests";
|
|
1534
|
-
|
|
1535
|
-
char *html;
|
|
1536
|
-
/* Use path relative to base_directory */
|
|
1537
|
-
const char *bib_file = "test_refs.bib";
|
|
1538
|
-
const char *bib_files[] = {bib_file, NULL};
|
|
1539
|
-
opts.bibliography_files = (char **)bib_files;
|
|
1540
|
-
|
|
1541
|
-
/* Test Pandoc citation syntax */
|
|
1542
|
-
const char *pandoc_cite = "See [@doe99] for details.";
|
|
1543
|
-
html = apex_markdown_to_html(pandoc_cite, strlen(pandoc_cite), &opts);
|
|
1544
|
-
assert_contains(html, "citation", "Pandoc citation generates citation class");
|
|
1545
|
-
assert_contains(html, "doe99", "Pandoc citation includes key");
|
|
1546
|
-
apex_free_string(html);
|
|
1547
|
-
|
|
1548
|
-
/* Test multiple Pandoc citations */
|
|
1549
|
-
const char *pandoc_multiple = "See [@doe99; @smith2000] for details.";
|
|
1550
|
-
html = apex_markdown_to_html(pandoc_multiple, strlen(pandoc_multiple), &opts);
|
|
1551
|
-
assert_contains(html, "doe99", "Multiple citations include first key");
|
|
1552
|
-
assert_contains(html, "smith2000", "Multiple citations include second key");
|
|
1553
|
-
apex_free_string(html);
|
|
1554
|
-
|
|
1555
|
-
/* Test author-in-text citation */
|
|
1556
|
-
const char *pandoc_author = "@smith04 says blah.";
|
|
1557
|
-
html = apex_markdown_to_html(pandoc_author, strlen(pandoc_author), &opts);
|
|
1558
|
-
assert_contains(html, "citation", "Author-in-text citation generates citation");
|
|
1559
|
-
assert_contains(html, "smith04", "Author-in-text citation includes key");
|
|
1560
|
-
apex_free_string(html);
|
|
1561
|
-
|
|
1562
|
-
/* Test MultiMarkdown citation syntax */
|
|
1563
|
-
opts.mode = APEX_MODE_MULTIMARKDOWN;
|
|
1564
|
-
const char *mmd_cite = "This is a statement[#Doe:2006].";
|
|
1565
|
-
html = apex_markdown_to_html(mmd_cite, strlen(mmd_cite), &opts);
|
|
1566
|
-
assert_contains(html, "citation", "MultiMarkdown citation generates citation class");
|
|
1567
|
-
assert_contains(html, "Doe:2006", "MultiMarkdown citation includes key");
|
|
1568
|
-
apex_free_string(html);
|
|
1569
|
-
|
|
1570
|
-
/* Test mmark citation syntax */
|
|
1571
|
-
opts.mode = APEX_MODE_UNIFIED;
|
|
1572
|
-
const char *mmark_cite = "This references [@RFC2535].";
|
|
1573
|
-
html = apex_markdown_to_html(mmark_cite, strlen(mmark_cite), &opts);
|
|
1574
|
-
assert_contains(html, "citation", "mmark citation generates citation class");
|
|
1575
|
-
assert_contains(html, "RFC2535", "mmark citation includes key");
|
|
1576
|
-
apex_free_string(html);
|
|
1577
|
-
|
|
1578
|
-
/* Test bibliography generation - use metadata to ensure bibliography loads */
|
|
1579
|
-
const char *with_refs = "---\nbibliography: test_refs.bib\n---\n\nSee [@doe99].\n\n<!-- REFERENCES -->";
|
|
1580
|
-
html = apex_markdown_to_html(with_refs, strlen(with_refs), &opts);
|
|
1581
|
-
if (strstr(html, "<div id=\"refs\"")) {
|
|
1582
|
-
assert_contains(html, "ref-doe99", "Bibliography includes cited entry");
|
|
1583
|
-
assert_contains(html, "Doe, John", "Bibliography includes author");
|
|
1584
|
-
assert_contains(html, "1999", "Bibliography includes year");
|
|
1585
|
-
assert_not_contains(html, "<!-- REFERENCES -->", "Bibliography marker replaced");
|
|
1586
|
-
assert_contains(html, "Article Title", "Bibliography includes article title");
|
|
1587
|
-
assert_contains(html, "Journal Name", "Bibliography includes journal");
|
|
1588
|
-
} else {
|
|
1589
|
-
/* If bibliography didn't load, at least verify citation was processed */
|
|
1590
|
-
assert_contains(html, "citation", "Citation was processed");
|
|
1591
|
-
/* Skip 5 tests - bibliography file may not load in test context */
|
|
1592
|
-
for (int i = 0; i < 5; i++) {
|
|
1593
|
-
test_result(true, "Bibliography tests skipped (file may not load in test context)");
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
apex_free_string(html);
|
|
1597
|
-
|
|
1598
|
-
/* Test that citations don't interfere with autolinking */
|
|
1599
|
-
apex_options opts_autolink = apex_options_default();
|
|
1600
|
-
opts_autolink.mode = APEX_MODE_UNIFIED;
|
|
1601
|
-
opts_autolink.enable_autolink = true;
|
|
1602
|
-
opts_autolink.enable_citations = false; /* Disable citations for this test */
|
|
1603
|
-
opts_autolink.bibliography_files = NULL;
|
|
1604
|
-
const char *no_cite_email = "Contact me at test@example.com";
|
|
1605
|
-
html = apex_markdown_to_html(no_cite_email, strlen(no_cite_email), &opts_autolink);
|
|
1606
|
-
assert_contains(html, "mailto:", "Email autolinking still works");
|
|
1607
|
-
apex_free_string(html);
|
|
1608
|
-
|
|
1609
|
-
/* Test markdown mailto links with @ and query params are not re-autolinked */
|
|
1610
|
-
const char *mailto_markdown =
|
|
1611
|
-
"If you have purchased a permanent unlock or lifetime license through the Mac App Store, please "
|
|
1612
|
-
"[email the developer](mailto:marked@brettterpstra.com?subject=Marked%20License%20Crossgrade&body=Please%20include%20your%20UUID%20%28Help-%3ECopy%20UUID%20in%20Marked%29%20in%20this%20email%20for%20receipt%20verification.) "
|
|
1613
|
-
"to request a free lifetime Paddle license.";
|
|
1614
|
-
html = apex_markdown_to_html(mailto_markdown, strlen(mailto_markdown), &opts_autolink);
|
|
1615
|
-
assert_contains(
|
|
1616
|
-
html,
|
|
1617
|
-
"<a href=\"mailto:marked@brettterpstra.com?subject=Marked%20License%20Crossgrade&body=Please%20include%20your%20UUID%20%28Help-%3ECopy%20UUID%20in%20Marked%29%20in%20this%20email%20for%20receipt%20verification.\">email the developer</a>",
|
|
1618
|
-
"Markdown mailto link with query params renders as a single clean anchor");
|
|
1619
|
-
assert_not_contains(html, "[email the", "Markdown link syntax is not leaked into output");
|
|
1620
|
-
assert_not_contains(html, "mailto:developer](", "No nested autolink corruption in mailto link");
|
|
1621
|
-
apex_free_string(html);
|
|
1622
|
-
|
|
1623
|
-
/* Test that autolink does not run inside indented code blocks */
|
|
1624
|
-
const char *indented_code =
|
|
1625
|
-
" x-marked://extract?url=https://example.com\n";
|
|
1626
|
-
html = apex_markdown_to_html(indented_code, strlen(indented_code), &opts_autolink);
|
|
1627
|
-
assert_not_contains(html, "[https://example.com](https://example.com)",
|
|
1628
|
-
"Indented code block URL is not autolinked");
|
|
1629
|
-
assert_contains(html, "x-marked://extract?url=https://example.com",
|
|
1630
|
-
"Indented code block content is preserved");
|
|
1631
|
-
apex_free_string(html);
|
|
1632
|
-
|
|
1633
|
-
/* Test that @ in citations doesn't become mailto */
|
|
1634
|
-
const char *cite_with_at = "See [@doe99] for details.";
|
|
1635
|
-
html = apex_markdown_to_html(cite_with_at, strlen(cite_with_at), &opts);
|
|
1636
|
-
assert_not_contains(html, "mailto:doe99", "@ in citation doesn't become mailto link");
|
|
1637
|
-
assert_contains(html, "citation", "Citation still processed correctly");
|
|
1638
|
-
apex_free_string(html);
|
|
1639
|
-
|
|
1640
|
-
/* Test that citations are not processed when bibliography is not provided */
|
|
1641
|
-
apex_options opts_no_bib = apex_options_default();
|
|
1642
|
-
opts_no_bib.mode = APEX_MODE_UNIFIED;
|
|
1643
|
-
opts_no_bib.enable_citations = true;
|
|
1644
|
-
opts_no_bib.bibliography_files = NULL;
|
|
1645
|
-
const char *cite_no_bib = "See [@doe99] for details.";
|
|
1646
|
-
html = apex_markdown_to_html(cite_no_bib, strlen(cite_no_bib), &opts_no_bib);
|
|
1647
|
-
/* Citation syntax should not be processed when no bibliography */
|
|
1648
|
-
assert_not_contains(html, "citation", "Citations not processed without bibliography");
|
|
1649
|
-
apex_free_string(html);
|
|
1650
|
-
|
|
1651
|
-
/* Test metadata bibliography */
|
|
1652
|
-
const char *md_with_bib = "---\nbibliography: test_refs.bib\n---\n\nSee [@doe99].";
|
|
1653
|
-
apex_options opts_meta = apex_options_default();
|
|
1654
|
-
opts_meta.mode = APEX_MODE_UNIFIED;
|
|
1655
|
-
opts_meta.base_directory = "tests";
|
|
1656
|
-
html = apex_markdown_to_html(md_with_bib, strlen(md_with_bib), &opts_meta);
|
|
1657
|
-
assert_contains(html, "citation", "Metadata bibliography enables citations");
|
|
1658
|
-
assert_contains(html, "doe99", "Metadata bibliography processes citations");
|
|
1659
|
-
apex_free_string(html);
|
|
1660
|
-
|
|
1661
|
-
/* Test suppress bibliography option */
|
|
1662
|
-
opts.suppress_bibliography = true;
|
|
1663
|
-
const char *suppress_test = "See [@doe99].\n\n<!-- REFERENCES -->";
|
|
1664
|
-
html = apex_markdown_to_html(suppress_test, strlen(suppress_test), &opts);
|
|
1665
|
-
assert_not_contains(html, "<div id=\"refs\"", "Bibliography suppressed when flag set");
|
|
1666
|
-
apex_free_string(html);
|
|
1667
|
-
|
|
1668
|
-
/* Test link citations option */
|
|
1669
|
-
opts.suppress_bibliography = false;
|
|
1670
|
-
opts.link_citations = true;
|
|
1671
|
-
const char *link_test = "See [@doe99].";
|
|
1672
|
-
html = apex_markdown_to_html(link_test, strlen(link_test), &opts);
|
|
1673
|
-
assert_contains(html, "<a href=\"#ref-doe99\"", "Citations linked when link_citations enabled");
|
|
1674
|
-
assert_contains(html, "class=\"citation\"", "Linked citations have citation class");
|
|
1675
|
-
apex_free_string(html);
|
|
1676
|
-
|
|
1677
|
-
/* Test author-in-text locator parsing: @key [p. 2] */
|
|
1678
|
-
{
|
|
1679
|
-
apex_options loc = opts;
|
|
1680
|
-
loc.mode = APEX_MODE_UNIFIED;
|
|
1681
|
-
loc.link_citations = true;
|
|
1682
|
-
const char *author_locator = "According to @smith04 [p. 2], ok.";
|
|
1683
|
-
html = apex_markdown_to_html(author_locator, strlen(author_locator), &loc);
|
|
1684
|
-
assert_contains(html, "data-cites=\"smith04\"", "Author-in-text locator citation processed");
|
|
1685
|
-
assert_not_contains(html, "p. 2", "Author-in-text locator consumed (not left in output)");
|
|
1686
|
-
apex_free_string(html);
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
/* Test brace-wrapped citation key path: allow keys starting with non-alnum (e.g. '/').
|
|
1690
|
-
* Even if the key doesn't resolve to a bibliography entry, it should still parse.
|
|
1691
|
-
*/
|
|
1692
|
-
{
|
|
1693
|
-
apex_options brace = opts;
|
|
1694
|
-
brace.mode = APEX_MODE_UNIFIED;
|
|
1695
|
-
const char *brace_key = "See [@{/foo}].";
|
|
1696
|
-
html = apex_markdown_to_html(brace_key, strlen(brace_key), &brace);
|
|
1697
|
-
assert_contains(html, "class=\"citation\"", "Brace-wrapped key produces citation span");
|
|
1698
|
-
assert_contains(html, "data-cites=\"/foo\"", "Brace-wrapped key preserved in data-cites");
|
|
1699
|
-
assert_contains(html, "(/foo)", "Unresolved brace-wrapped key rendered as literal key");
|
|
1700
|
-
apex_free_string(html);
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
/* Test citations enabled but bibliography file missing/unreadable:
|
|
1704
|
-
* should still parse citation syntax, but render unresolved key.
|
|
1705
|
-
*/
|
|
1706
|
-
{
|
|
1707
|
-
apex_options missing = apex_options_default();
|
|
1708
|
-
missing.mode = APEX_MODE_UNIFIED;
|
|
1709
|
-
missing.enable_citations = true;
|
|
1710
|
-
missing.base_directory = "tests";
|
|
1711
|
-
const char *missing_bib = "missing.bib";
|
|
1712
|
-
const char *missing_bibs[] = { missing_bib, NULL };
|
|
1713
|
-
missing.bibliography_files = (char **)missing_bibs;
|
|
1714
|
-
|
|
1715
|
-
const char *cite_missing = "See [@doe99].";
|
|
1716
|
-
html = apex_markdown_to_html(cite_missing, strlen(cite_missing), &missing);
|
|
1717
|
-
assert_contains(html, "class=\"citation\"", "Missing bibliography: citation still processed");
|
|
1718
|
-
assert_contains(html, "data-cites=\"doe99\"", "Missing bibliography: key preserved");
|
|
1719
|
-
assert_contains(html, "(doe99)", "Missing bibliography: unresolved key rendered");
|
|
1720
|
-
apex_free_string(html);
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
bool had_failures = suite_end(suite_failures);
|
|
1724
|
-
print_suite_title("Citation and Bibliography Tests", had_failures, false);
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
/**
|
|
1729
|
-
* Test metadata control of command-line options
|
|
1730
|
-
*/
|
|
1731
|
-
|
|
1732
|
-
void test_aria_labels(void) {
|
|
1733
|
-
int suite_failures = suite_start();
|
|
1734
|
-
print_suite_title("ARIA Labels Tests", false, true);
|
|
1735
|
-
|
|
1736
|
-
apex_options opts = apex_options_default();
|
|
1737
|
-
char *html;
|
|
1738
|
-
|
|
1739
|
-
/* Test 1: TOC nav gets aria-label when --aria is enabled */
|
|
1740
|
-
opts.enable_aria = true;
|
|
1741
|
-
opts.enable_marked_extensions = true;
|
|
1742
|
-
const char *doc_with_toc = "# Header 1\n\n<!--TOC-->\n\n## Header 2";
|
|
1743
|
-
html = apex_markdown_to_html(doc_with_toc, strlen(doc_with_toc), &opts);
|
|
1744
|
-
assert_contains(html, "<nav class=\"toc\"", "TOC nav element present");
|
|
1745
|
-
assert_contains(html, "aria-label=\"Table of contents\"", "TOC nav has aria-label");
|
|
1746
|
-
apex_free_string(html);
|
|
1747
|
-
|
|
1748
|
-
/* Test 2: TOC nav does NOT get aria-label when --aria is disabled (backward compatibility) */
|
|
1749
|
-
opts.enable_aria = false;
|
|
1750
|
-
html = apex_markdown_to_html(doc_with_toc, strlen(doc_with_toc), &opts);
|
|
1751
|
-
assert_contains(html, "<nav class=\"toc\"", "TOC nav element present");
|
|
1752
|
-
assert_not_contains(html, "aria-label=\"Table of contents\"", "TOC nav without aria-label when disabled");
|
|
1753
|
-
apex_free_string(html);
|
|
1754
|
-
|
|
1755
|
-
/* Test 3: Figures get role="figure" when --aria is enabled */
|
|
1756
|
-
opts.enable_aria = true;
|
|
1757
|
-
opts.enable_tables = true;
|
|
1758
|
-
/* Use a table with caption to generate a figure */
|
|
1759
|
-
const char *table_with_caption = "[Test Table Caption]\n| A | B |\n|---|---|\n| 1 | 2 |";
|
|
1760
|
-
html = apex_markdown_to_html(table_with_caption, strlen(table_with_caption), &opts);
|
|
1761
|
-
assert_contains(html, "<figure", "Figure element present");
|
|
1762
|
-
assert_contains(html, "role=\"figure\"", "Figure has role attribute");
|
|
1763
|
-
apex_free_string(html);
|
|
1764
|
-
|
|
1765
|
-
/* Test 4: Figures do NOT get role when --aria is disabled */
|
|
1766
|
-
opts.enable_aria = false;
|
|
1767
|
-
html = apex_markdown_to_html(table_with_caption, strlen(table_with_caption), &opts);
|
|
1768
|
-
assert_contains(html, "<figure", "Figure element present");
|
|
1769
|
-
assert_not_contains(html, "role=\"figure\"", "Figure without role when disabled");
|
|
1770
|
-
apex_free_string(html);
|
|
1771
|
-
|
|
1772
|
-
/* Test 5: Tables get role="table" when --aria is enabled */
|
|
1773
|
-
opts.enable_aria = true;
|
|
1774
|
-
const char *simple_table = "| A | B |\n|---|---|\n| 1 | 2 |";
|
|
1775
|
-
html = apex_markdown_to_html(simple_table, strlen(simple_table), &opts);
|
|
1776
|
-
assert_contains(html, "<table", "Table element present");
|
|
1777
|
-
assert_contains(html, "role=\"table\"", "Table has role attribute");
|
|
1778
|
-
apex_free_string(html);
|
|
1779
|
-
|
|
1780
|
-
/* Test 6: Tables do NOT get role when --aria is disabled */
|
|
1781
|
-
opts.enable_aria = false;
|
|
1782
|
-
html = apex_markdown_to_html(simple_table, strlen(simple_table), &opts);
|
|
1783
|
-
assert_contains(html, "<table", "Table element present");
|
|
1784
|
-
assert_not_contains(html, "role=\"table\"", "Table without role when disabled");
|
|
1785
|
-
apex_free_string(html);
|
|
1786
|
-
|
|
1787
|
-
/* Test 7: Table figures with captions get IDs on figcaption when --aria is enabled */
|
|
1788
|
-
opts.enable_aria = true;
|
|
1789
|
-
html = apex_markdown_to_html(table_with_caption, strlen(table_with_caption), &opts);
|
|
1790
|
-
assert_contains(html, "<figcaption", "Figcaption element present");
|
|
1791
|
-
assert_contains(html, "id=\"table-caption-1\"", "Figcaption has generated ID");
|
|
1792
|
-
apex_free_string(html);
|
|
1793
|
-
|
|
1794
|
-
/* Test 8: Tables with captions get aria-describedby linking to figcaption when --aria is enabled */
|
|
1795
|
-
opts.enable_aria = true;
|
|
1796
|
-
html = apex_markdown_to_html(table_with_caption, strlen(table_with_caption), &opts);
|
|
1797
|
-
assert_contains(html, "<table", "Table element present");
|
|
1798
|
-
assert_contains(html, "aria-describedby=\"table-caption-1\"", "Table has aria-describedby linking to caption");
|
|
1799
|
-
apex_free_string(html);
|
|
1800
|
-
|
|
1801
|
-
/* Test 9: Multiple tables with captions get unique IDs */
|
|
1802
|
-
opts.enable_aria = true;
|
|
1803
|
-
const char *multiple_tables =
|
|
1804
|
-
"[First Table]\n| A |\n|---|\n| 1 |\n\n"
|
|
1805
|
-
"[Second Table]\n| B |\n|---|\n| 2 |";
|
|
1806
|
-
html = apex_markdown_to_html(multiple_tables, strlen(multiple_tables), &opts);
|
|
1807
|
-
assert_contains(html, "id=\"table-caption-1\"", "First figcaption has ID 1");
|
|
1808
|
-
assert_contains(html, "id=\"table-caption-2\"", "Second figcaption has ID 2");
|
|
1809
|
-
assert_contains(html, "aria-describedby=\"table-caption-1\"", "First table links to caption 1");
|
|
1810
|
-
assert_contains(html, "aria-describedby=\"table-caption-2\"", "Second table links to caption 2");
|
|
1811
|
-
apex_free_string(html);
|
|
1812
|
-
|
|
1813
|
-
/* Test 10: Tables with existing figcaption IDs use them for aria-describedby */
|
|
1814
|
-
/* Note: IAL syntax on caption lines ([Caption]{#id}) is not currently supported.
|
|
1815
|
-
* This test verifies that regular captions work with generated IDs. */
|
|
1816
|
-
opts.enable_aria = true;
|
|
1817
|
-
const char *table_with_regular_caption = "[Custom Caption]\n| A |\n|---|\n| 1 |";
|
|
1818
|
-
html = apex_markdown_to_html(table_with_regular_caption, strlen(table_with_regular_caption), &opts);
|
|
1819
|
-
assert_contains(html, "<figcaption", "Figcaption element present");
|
|
1820
|
-
assert_contains(html, "id=\"table-caption-", "Figcaption has generated ID");
|
|
1821
|
-
assert_contains(html, "aria-describedby=\"table-caption-", "Table links to figcaption ID");
|
|
1822
|
-
apex_free_string(html);
|
|
1823
|
-
|
|
1824
|
-
/* Test 11: TOC with MMD style also gets aria-label */
|
|
1825
|
-
opts.enable_aria = true;
|
|
1826
|
-
const char *mmd_toc_doc = "# Title\n\n{{TOC}}\n\n## Section";
|
|
1827
|
-
html = apex_markdown_to_html(mmd_toc_doc, strlen(mmd_toc_doc), &opts);
|
|
1828
|
-
assert_contains(html, "<nav class=\"toc\"", "MMD TOC nav present");
|
|
1829
|
-
assert_contains(html, "aria-label=\"Table of contents\"", "MMD TOC nav has aria-label");
|
|
1830
|
-
apex_free_string(html);
|
|
1831
|
-
|
|
1832
|
-
bool had_failures = suite_end(suite_failures);
|
|
1833
|
-
print_suite_title("ARIA Labels Tests", had_failures, false);
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
/**
|
|
1837
|
-
* Main test runner
|
|
1838
|
-
*/
|
|
1839
|
-
|
|
1840
|
-
void test_combine_gitbook_like(void) {
|
|
1841
|
-
int suite_failures = suite_start();
|
|
1842
|
-
print_suite_title("Combine / GitBook SUMMARY-like Tests", false, true);
|
|
1843
|
-
|
|
1844
|
-
apex_options opts = apex_options_default();
|
|
1845
|
-
opts.enable_file_includes = true;
|
|
1846
|
-
opts.generate_header_ids = false; /* Disable header IDs for these tests */
|
|
1847
|
-
|
|
1848
|
-
const char *base_dir = "tests/fixtures/combine_summary";
|
|
1849
|
-
|
|
1850
|
-
const char *intro_path = "tests/fixtures/combine_summary/intro.md";
|
|
1851
|
-
const char *chapter_path = "tests/fixtures/combine_summary/chapter1.md";
|
|
1852
|
-
|
|
1853
|
-
/* Intro alone */
|
|
1854
|
-
size_t intro_len = 0;
|
|
1855
|
-
char *intro_src = NULL;
|
|
1856
|
-
{
|
|
1857
|
-
FILE *fp = fopen(intro_path, "rb");
|
|
1858
|
-
if (fp) {
|
|
1859
|
-
fseek(fp, 0, SEEK_END);
|
|
1860
|
-
long sz = ftell(fp);
|
|
1861
|
-
fseek(fp, 0, SEEK_SET);
|
|
1862
|
-
intro_src = malloc(sz + 1);
|
|
1863
|
-
if (intro_src) {
|
|
1864
|
-
intro_len = fread(intro_src, 1, sz, fp);
|
|
1865
|
-
intro_src[intro_len] = '\0';
|
|
1866
|
-
}
|
|
1867
|
-
fclose(fp);
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
if (!intro_src) {
|
|
1871
|
-
printf(COLOR_RED "✗" COLOR_RESET " Failed to read intro fixture for combine tests\n");
|
|
1872
|
-
tests_failed++;
|
|
1873
|
-
tests_run++;
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
/* Process intro with includes (none here, just sanity) */
|
|
1878
|
-
char *intro_md = apex_process_includes(intro_src, base_dir, NULL, 0, NULL, NULL, NULL);
|
|
1879
|
-
char *intro_html = apex_markdown_to_html(intro_md ? intro_md : intro_src,
|
|
1880
|
-
strlen(intro_md ? intro_md : intro_src),
|
|
1881
|
-
&opts);
|
|
1882
|
-
assert_contains(intro_html, "<h1>Intro</h1>", "Intro heading rendered");
|
|
1883
|
-
apex_free_string(intro_html);
|
|
1884
|
-
if (intro_md) free(intro_md);
|
|
1885
|
-
free(intro_src);
|
|
1886
|
-
|
|
1887
|
-
/* Now chapter1 which includes section1_1.md via {{section1_1.md}} */
|
|
1888
|
-
size_t chapter_len = 0;
|
|
1889
|
-
char *chapter_src = NULL;
|
|
1890
|
-
{
|
|
1891
|
-
FILE *fp = fopen(chapter_path, "rb");
|
|
1892
|
-
if (fp) {
|
|
1893
|
-
fseek(fp, 0, SEEK_END);
|
|
1894
|
-
long sz = ftell(fp);
|
|
1895
|
-
fseek(fp, 0, SEEK_SET);
|
|
1896
|
-
chapter_src = malloc(sz + 1);
|
|
1897
|
-
if (chapter_src) {
|
|
1898
|
-
chapter_len = fread(chapter_src, 1, sz, fp);
|
|
1899
|
-
chapter_src[chapter_len] = '\0';
|
|
1900
|
-
}
|
|
1901
|
-
fclose(fp);
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
if (!chapter_src) {
|
|
1905
|
-
printf(COLOR_RED "✗" COLOR_RESET " Failed to read chapter1 fixture for combine tests\n");
|
|
1906
|
-
tests_failed++;
|
|
1907
|
-
tests_run++;
|
|
1908
|
-
return;
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
char *chapter_md = apex_process_includes(chapter_src, base_dir, NULL, 0, NULL, NULL, NULL);
|
|
1912
|
-
const char *chapter_final = chapter_md ? chapter_md : chapter_src;
|
|
1913
|
-
|
|
1914
|
-
/* Combined Markdown should contain both Chapter 1 and Section 1.1 headings */
|
|
1915
|
-
if (strstr(chapter_final, "# Chapter 1") && strstr(chapter_final, "# Section 1.1")) {
|
|
1916
|
-
test_result(true, "Includes expanded for chapter1/section1_1");
|
|
1917
|
-
} else {
|
|
1918
|
-
test_result(false, "Includes not expanded for chapter1/section1_1");
|
|
1919
|
-
printf(COLOR_RED "✗" COLOR_RESET " Includes not expanded correctly for chapter1/section1_1\n");
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
char *chapter_html = apex_markdown_to_html(chapter_final, strlen(chapter_final), &opts);
|
|
1923
|
-
assert_contains(chapter_html, "<h1>Chapter 1</h1>", "Chapter 1 heading rendered");
|
|
1924
|
-
assert_contains(chapter_html, "<h1>Section 1.1</h1>", "Section 1.1 heading rendered from included file");
|
|
1925
|
-
|
|
1926
|
-
apex_free_string(chapter_html);
|
|
1927
|
-
if (chapter_md) free(chapter_md);
|
|
1928
|
-
free(chapter_src);
|
|
1929
|
-
|
|
1930
|
-
bool had_failures = suite_end(suite_failures);
|
|
1931
|
-
print_suite_title("Combine / GitBook SUMMARY-like Tests", had_failures, false);
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
/**
|
|
1935
|
-
* Test Pandoc JSON parser with filter-style output (Header + RawBlock with
|
|
1936
|
-
* escaped quotes and newlines in the raw string).
|
|
1937
|
-
*/
|
|
1938
|
-
void test_ast_json_parser(void) {
|
|
1939
|
-
int suite_failures = suite_start();
|
|
1940
|
-
print_suite_title("AST JSON Parser (filter output with RawBlock)", false, true);
|
|
1941
|
-
|
|
1942
|
-
/* Minimal: only RawBlock with no escapes – should parse to 1 child */
|
|
1943
|
-
const char *minimal = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"t\":\"RawBlock\",\"c\":[\"html\",\"ab\"]}]}";
|
|
1944
|
-
apex_options opts0 = apex_options_default();
|
|
1945
|
-
cmark_node *doc0 = apex_pandoc_json_to_cmark(minimal, &opts0);
|
|
1946
|
-
int minimal_ok = (doc0 && cmark_node_first_child(doc0) && !cmark_node_next(cmark_node_first_child(doc0)));
|
|
1947
|
-
if (doc0) cmark_node_free(doc0);
|
|
1948
|
-
test_result(minimal_ok, "Minimal RawBlock (no escapes) parses to one block");
|
|
1949
|
-
|
|
1950
|
-
/* Single RawBlock with the exact long string (escaped quotes + newlines) */
|
|
1951
|
-
const char *single_raw = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"t\":\"RawBlock\",\"c\":[\"html\",\"<figure><p>< <img src=\\\"image.png\\\" alt=\\\"Image\\\" /></p>\\n</figure>\\n\"]}]}";
|
|
1952
|
-
cmark_node *doc1 = apex_pandoc_json_to_cmark(single_raw, &opts0);
|
|
1953
|
-
int single_ok = (doc1 && cmark_node_first_child(doc1) && !cmark_node_next(cmark_node_first_child(doc1)));
|
|
1954
|
-
if (doc1) cmark_node_free(doc1);
|
|
1955
|
-
test_result(single_ok, "Single RawBlock with \\\" and \\n in content parses");
|
|
1956
|
-
|
|
1957
|
-
/* Header only – should parse to 1 child */
|
|
1958
|
-
const char *header_only = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"t\":\"Header\",\"c\":[1,[\"\",[],[]],[{\"t\":\"Str\",\"c\":\"UNWRAP FILTER\"}]}]}";
|
|
1959
|
-
cmark_node *doc_h = apex_pandoc_json_to_cmark(header_only, &opts0);
|
|
1960
|
-
int header_ok = (doc_h && cmark_node_first_child(doc_h) && !cmark_node_next(cmark_node_first_child(doc_h)));
|
|
1961
|
-
if (doc_h) cmark_node_free(doc_h);
|
|
1962
|
-
test_result(header_ok, "Header-only document parses to one block");
|
|
1963
|
-
|
|
1964
|
-
/* Round-trip: build Header + RawBlock, serialize to JSON, parse back → must get 2 blocks */
|
|
1965
|
-
{
|
|
1966
|
-
cmark_node *build = cmark_node_new(CMARK_NODE_DOCUMENT);
|
|
1967
|
-
cmark_node *h = cmark_node_new(CMARK_NODE_HEADING);
|
|
1968
|
-
cmark_node_set_heading_level(h, 1);
|
|
1969
|
-
cmark_node *htext = cmark_node_new(CMARK_NODE_TEXT);
|
|
1970
|
-
cmark_node_set_literal(htext, "Title");
|
|
1971
|
-
cmark_node_append_child(h, htext);
|
|
1972
|
-
cmark_node_append_child(build, h);
|
|
1973
|
-
cmark_node *raw = cmark_node_new(CMARK_NODE_HTML_BLOCK);
|
|
1974
|
-
cmark_node_set_literal(raw, "<p>x</p>");
|
|
1975
|
-
cmark_node_append_child(build, raw);
|
|
1976
|
-
char *two_block_json = apex_cmark_to_pandoc_json(build, &opts0);
|
|
1977
|
-
cmark_node_free(build);
|
|
1978
|
-
int round_ok = 0;
|
|
1979
|
-
if (two_block_json) {
|
|
1980
|
-
cmark_node *back = apex_pandoc_json_to_cmark(two_block_json, &opts0);
|
|
1981
|
-
if (back) {
|
|
1982
|
-
int n = 0;
|
|
1983
|
-
for (cmark_node *b = cmark_node_first_child(back); b; b = cmark_node_next(b)) n++;
|
|
1984
|
-
round_ok = (n == 2);
|
|
1985
|
-
cmark_node_free(back);
|
|
1986
|
-
}
|
|
1987
|
-
free(two_block_json);
|
|
1988
|
-
}
|
|
1989
|
-
test_result(round_ok, "Round-trip (Header + RawBlock) JSON parses to two blocks");
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
/* Full filter-style: Header + RawBlock; single literal so run-time has },{ with no stray quote */
|
|
1993
|
-
const char *json = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"t\":\"Header\",\"c\":[1,[\"\",[],[]],[{\"t\":\"Str\",\"c\":\"UNWRAP FILTER\"}]]},{\"t\":\"RawBlock\",\"c\":[\"html\",\"<figure><p>< <img src=\\\"image.png\\\" alt=\\\"Image\\\" /></p>\\n</figure>\\n\"]}]}";
|
|
1994
|
-
|
|
1995
|
-
/* Same payload but with "c" before "t" (dkjson order) for second block. Ensure comma between blocks. */
|
|
1996
|
-
const char *json_c_before_t = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"t\":\"Header\",\"c\":[1,[\"\",[],[]],[{\"t\":\"Str\",\"c\":\"X\"}]]},{\"c\":[\"html\",\"<x/>\"],\"t\":\"RawBlock\"}]}";
|
|
1997
|
-
|
|
1998
|
-
apex_options opts = apex_options_default();
|
|
1999
|
-
cmark_node *doc = apex_pandoc_json_to_cmark(json, &opts);
|
|
2000
|
-
if (!doc) {
|
|
2001
|
-
test_result(false, "apex_pandoc_json_to_cmark returned NULL");
|
|
2002
|
-
bool had_failures = suite_end(suite_failures);
|
|
2003
|
-
print_suite_title("AST JSON Parser (filter output with RawBlock)", had_failures, false);
|
|
2004
|
-
return;
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
cmark_node *first = cmark_node_first_child(doc);
|
|
2008
|
-
int count = 0;
|
|
2009
|
-
for (cmark_node *n = first; n; n = cmark_node_next(n)) count++;
|
|
2010
|
-
test_result(count == 2, "Parsed document has two block children (Header + RawBlock)");
|
|
2011
|
-
if (count >= 1) {
|
|
2012
|
-
cmark_node_type t1 = cmark_node_get_type(first);
|
|
2013
|
-
test_result(t1 == CMARK_NODE_HEADING, "First block is heading");
|
|
2014
|
-
}
|
|
2015
|
-
if (count >= 2) {
|
|
2016
|
-
cmark_node *second = cmark_node_next(first);
|
|
2017
|
-
cmark_node_type t2 = cmark_node_get_type(second);
|
|
2018
|
-
test_result(t2 == CMARK_NODE_HTML_BLOCK, "Second block is HTML block");
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
cmark_node_free(doc);
|
|
2022
|
-
|
|
2023
|
-
/* Minimal two-block: Para + RawBlock (no Header nesting) */
|
|
2024
|
-
const char *minimal_two = "{\"blocks\":[{\"t\":\"Para\",\"c\":[]},{\"t\":\"RawBlock\",\"c\":[\"html\",\"x\"]}]}";
|
|
2025
|
-
cmark_node *doc_min = apex_pandoc_json_to_cmark(minimal_two, &opts);
|
|
2026
|
-
int count_min = 0;
|
|
2027
|
-
if (doc_min) {
|
|
2028
|
-
for (cmark_node *n = cmark_node_first_child(doc_min); n; n = cmark_node_next(n)) count_min++;
|
|
2029
|
-
cmark_node_free(doc_min);
|
|
2030
|
-
}
|
|
2031
|
-
test_result(doc_min && count_min == 2, "Minimal two-block (Para + RawBlock) parses to two blocks");
|
|
2032
|
-
|
|
2033
|
-
/* Single RawBlock with "c" before "t" (dkjson order) – must parse to 1 block */
|
|
2034
|
-
const char *single_c_before_t = "{\"pandoc-api-version\":[1,23,1],\"meta\":{},\"blocks\":[{\"c\":[\"html\",\"<x/>\"],\"t\":\"RawBlock\"}]}";
|
|
2035
|
-
cmark_node *doc1cb = apex_pandoc_json_to_cmark(single_c_before_t, &opts);
|
|
2036
|
-
int count1cb = 0;
|
|
2037
|
-
if (doc1cb) {
|
|
2038
|
-
for (cmark_node *n = cmark_node_first_child(doc1cb); n; n = cmark_node_next(n)) count1cb++;
|
|
2039
|
-
cmark_node_free(doc1cb);
|
|
2040
|
-
}
|
|
2041
|
-
test_result(doc1cb && count1cb == 1, "Single RawBlock with \"c\" before \"t\" parses to one block");
|
|
2042
|
-
|
|
2043
|
-
/* dkjson-style key order: "c" before "t" for second block – parser must still yield 2 blocks */
|
|
2044
|
-
cmark_node *doc2 = apex_pandoc_json_to_cmark(json_c_before_t, &opts);
|
|
2045
|
-
int count2 = 0;
|
|
2046
|
-
if (doc2) {
|
|
2047
|
-
for (cmark_node *n = cmark_node_first_child(doc2); n; n = cmark_node_next(n)) count2++;
|
|
2048
|
-
cmark_node_free(doc2);
|
|
2049
|
-
}
|
|
2050
|
-
test_result(doc2 && count2 == 2, "Header + RawBlock with \"c\" before \"t\" parses to two blocks");
|
|
2051
|
-
|
|
2052
|
-
bool had_failures = suite_end(suite_failures);
|
|
2053
|
-
print_suite_title("AST JSON Parser (filter output with RawBlock)", had_failures, false);
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
|