diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 52b11892..4d69fb7c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,32 +15,32 @@ jobs: matrix: rust-version: ['1.71.1', 'stable', 'nightly'] runs-on: ubuntu-latest - defaults: - run: - working-directory: pulldown-cmark steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup default ${{ matrix.rust-version }} + - name: Cargo fmt + if: ${{ matrix.rust-version == 'stable' }} + run: cargo fmt --check - name: Cargo build + if: ${{ matrix.rust-version == '1.71.1' }} run: cargo build --verbose + - name: Cargo build the workspace + if: ${{ matrix.rust-version != '1.71.1' }} + run: cargo build --workspace --verbose - name: Cargo test + # dos-fuzzer does not build with old rust version + if: ${{ matrix.rust-version == '1.71.1' }} run: cargo test --verbose + - name: Cargo test the workspace + if: ${{ matrix.rust-version != '1.71.1' }} + run: cargo test --verbose --workspace - name: Cargo test with simd feature enabled run: cargo test --features=simd,gen-tests - name: Cargo test with serde feature enabled run: cargo test --features=serde - - name: Cargo test without default features - run: cargo test --no-default-features - pulldown-cmark-escape: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: rustup default stable - - name: Cargo test - working-directory: pulldown-cmark-escape - run: cargo test --verbose --all + - name: Cargo test in no_std environment + run: cargo test --no-default-features --features hashbrown regression: runs-on: ubuntu-latest steps: @@ -49,8 +49,13 @@ jobs: working-directory: pulldown-cmark run: rustup default stable - name: Test for superlinear time regressions - working-directory: fuzzer + working-directory: dos-fuzzer run: cargo run --release -- --regressions - name: Check benchmarks are not broken working-directory: bench run: cargo check --benches + # Make sure the WASM target builds for the main package + - name: Add WASM target + run: rustup target add wasm32-unknown-unknown + - name: Build WASM target + run: cargo build --package pulldown-cmark --target wasm32-unknown-unknown diff --git a/.gitignore b/.gitignore index d913617b..8a1584fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,15 @@ target +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml +.envrc +devenv.nix +devenv.yaml +devenv.lock diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a2a46669..2f2d7101 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,9 +9,10 @@ frustration later on. ### Getting familiar with the project -**The architecture** is somewhat unique; it was originally inspired by [XML pull parsers](http://www.xmlpull.org), but ended up going in somewhat of its own direction. To get familiar with it, +**The architecture** is somewhat unique; it was originally inspired by [XML pull parsers](http://www.xmlpull.org), but ended up going in somewhat of its own direction. To get familiar with it, + - start by reading the [README](README.md) page, which gives some details on the design of the parser (pull-based events) and some rationalization for it; -- read the [blog post](https://fullyfaithful.eu/pulldown-cmark) about the release of pulldown-cmark 0.3 by Marcus Klaas de Vries. +- read the [blog post](https://web.archive.org/web/20220901143924/https://fullyfaithful.eu/pulldown-cmark/) about the release of pulldown-cmark 0.3 by Marcus Klaas de Vries. **The source code** can be approached by skimming the [API documentation](https://docs.rs/pulldown-cmark/latest/pulldown_cmark) first, then explore the code for the main struct, [`Parser`](https://docs.rs/pulldown-cmark/latest/pulldown_cmark/struct.Parser.html) diff --git a/Cargo.lock b/Cargo.lock index db591279..a6213bc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,32 +2,103 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bincode" @@ -38,17 +109,48 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" -version = "2.5.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "calendrical_calculations" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "e97f73e95d668625c9b28a3072e6326773785a0cf807de9f3d632778438f3d38" +dependencies = [ + "core_maths", + "displaydoc", +] [[package]] name = "cast" @@ -56,11 +158,32 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "ciborium" @@ -89,30 +212,90 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" -version = "4.5.4" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] [[package]] name = "criterion" @@ -126,7 +309,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -147,14 +330,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -171,280 +354,1393 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "either" -version = "1.11.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] -name = "getopts" -version = "0.2.21" +name = "deranged" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "unicode-width", + "powerfmt", ] [[package]] -name = "half" -version = "2.4.1" +name = "derive_arbitrary" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ - "cfg-if", - "crunchy", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "diff" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] -name = "is-terminal" -version = "0.4.12" +name = "diplomat" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "3137c640d2bac491dbfca7f9945c948f888dd8c95bdf7ee6b164fbdfa5d3efc2" dependencies = [ - "hermit-abi", - "libc", - "windows-sys", + "diplomat_core", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "diplomat-runtime" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "53bfcc833b58615b593a6e5c46771cb36b1cfce94899c60823810939fe8ca9d9" dependencies = [ - "either", + "log", ] [[package]] -name = "itoa" -version = "1.0.11" +name = "diplomat_core" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "cd7aca1d8f9e7b73ad61785beedc9556ad79f84b15c15abaa7041377e42284c1" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "serde", + "smallvec", + "strck_ident", + "syn", +] [[package]] -name = "js-sys" -version = "0.3.69" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "wasm-bindgen", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +name = "dos-fuzzer" +version = "0.1.0" +dependencies = [ + "clap", + "crossbeam-utils", + "itertools 0.10.5", + "libc", + "ndarray", + "ndarray-stats", + "num_cpus", + "pulldown-cmark", + "rand", + "rand_xoshiro", + "serde", + "serde_json", + "syn", + "walkdir", +] [[package]] -name = "libc" -version = "0.2.154" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "log" -version = "0.4.21" +name = "encoding_c" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "9af727805f3b0d79956bde5b35732669fb5c5d45a94893798e7b7e70cfbf9cc1" +dependencies = [ + "encoding_rs", +] [[package]] -name = "memchr" -version = "2.7.2" +name = "encoding_c_mem" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "3a80a16821fe8c7cab96e0c67b57cd7090e021e9615e6ce6ab0cf866c44ed1f0" +dependencies = [ + "encoding_rs", +] [[package]] -name = "num-traits" -version = "0.2.19" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "autocfg", + "cfg-if", ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "oorandom" -version = "11.1.3" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] [[package]] -name = "plotters" -version = "0.3.5" +name = "filetime" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", + "cfg-if", + "libc", + "libredox", ] [[package]] -name = "plotters-backend" -version = "0.3.5" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "plotters-svg" -version = "0.3.5" +name = "fixed_decimal" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8" dependencies = [ - "plotters-backend", + "displaydoc", + "ryu", + "smallvec", + "writeable", ] [[package]] -name = "proc-macro2" -version = "1.0.82" +name = "flate2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ - "unicode-ident", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "pulldown-cmark" -version = "0.11.0" -dependencies = [ - "bincode", - "bitflags", - "getopts", - "lazy_static", - "memchr", - "pulldown-cmark-escape", - "regex", - "serde", - "serde_json", - "unicase", -] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "pulldown-cmark-bench" -version = "0.0.0" +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ - "criterion", - "pulldown-cmark", + "unicode-width", ] [[package]] -name = "pulldown-cmark-escape" -version = "0.11.0" - -[[package]] -name = "quote" -version = "1.0.36" +name = "getrandom" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "proc-macro2", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "rayon" -version = "1.10.0" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "either", - "rayon-core", + "cfg-if", + "libc", + "r-efi", + "wasip2", ] [[package]] -name = "rayon-core" -version = "1.12.1" +name = "glob" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] -name = "regex" -version = "1.10.4" +name = "half" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "cfg-if", + "crunchy", + "zerocopy", ] [[package]] -name = "regex-automata" -version = "0.4.6" +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "allocator-api2", + "equivalent", + "foldhash", ] [[package]] -name = "regex-syntax" -version = "0.8.3" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "ryu" -version = "1.0.18" +name = "hermit-abi" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "same-file" -version = "1.0.6" +name = "home" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "winapi-util", + "windows-sys 0.61.2", ] [[package]] -name = "serde" -version = "1.0.202" +name = "icu_calendar" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff" dependencies = [ - "serde_derive", + "calendrical_calculations", + "displaydoc", + "icu_calendar_data", + "icu_locid", + "icu_locid_transform", + "icu_provider", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "serde_derive" -version = "1.0.202" +name = "icu_calendar_data" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" -dependencies = [ +checksum = "820499e77e852162190608b4f444e7b4552619150eafc39a9e39333d9efae9e1" + +[[package]] +name = "icu_capi" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f73a82a8307633c08ca119631cd90b006e448009da2d4466f7d76ca8fedf3b1" +dependencies = [ + "diplomat", + "diplomat-runtime", + "fixed_decimal", + "icu_calendar", + "icu_casemap", + "icu_collator", + "icu_collections", + "icu_datetime", + "icu_decimal", + "icu_experimental", + "icu_list", + "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_plurals", + "icu_properties", + "icu_provider", + "icu_provider_adapters", + "icu_segmenter", + "icu_timezone", + "log", + "simple_logger", + "tinystr", + "unicode-bidi", + "writeable", +] + +[[package]] +name = "icu_casemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f" +dependencies = [ + "displaydoc", + "icu_casemap_data", + "icu_collections", + "icu_locid", + "icu_properties", + "icu_provider", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_casemap_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd9f6276270c85a5cd54611adbbf94e993ec464a2a86a452a6c565b7ded5d9" + +[[package]] +name = "icu_collator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2" +dependencies = [ + "displaydoc", + "icu_collator_data", + "icu_collections", + "icu_locid_transform", + "icu_normalizer", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_collator_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b353986d77d28991eca4dea5ef2b8982f639342ae19ca81edc44f048bc38ebb" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_datetime" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780" +dependencies = [ + "displaydoc", + "either", + "fixed_decimal", + "icu_calendar", + "icu_datetime_data", + "icu_decimal", + "icu_locid", + "icu_locid_transform", + "icu_plurals", + "icu_provider", + "icu_timezone", + "smallvec", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_datetime_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef5f04076123cab1b7a926a7083db27fe0d7a0e575adb984854aae3f3a6507d" + +[[package]] +name = "icu_decimal" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_decimal_data", + "icu_locid_transform", + "icu_provider", + "writeable", +] + +[[package]] +name = "icu_decimal_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c95dd97f5ccf6d837a9c115496ec7d36646fa86ca18e7f1412115b4c820ae2" + +[[package]] +name = "icu_experimental" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_collections", + "icu_decimal", + "icu_experimental_data", + "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_pattern", + "icu_plurals", + "icu_properties", + "icu_provider", + "litemap", + "num-bigint", + "num-rational", + "num-traits", + "smallvec", + "tinystr", + "writeable", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_experimental_data" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121df92eafb8f5286d4e8ff401c1e7db8384377f806db3f8db77b91e5b7bd4dd" + +[[package]] +name = "icu_list" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365" +dependencies = [ + "displaydoc", + "icu_list_data", + "icu_locid_transform", + "icu_provider", + "regex-automata 0.2.0", + "writeable", +] + +[[package]] +name = "icu_list_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120" + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_pattern" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4" +dependencies = [ + "displaydoc", + "either", + "writeable", + "yoke", + "zerofrom", +] + +[[package]] +name = "icu_plurals" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114" +dependencies = [ + "displaydoc", + "fixed_decimal", + "icu_locid_transform", + "icu_plurals_data", + "icu_provider", + "zerovec", +] + +[[package]] +name = "icu_plurals_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a483403238cb7d6a876a77a5f8191780336d80fe7b8b00bfdeb20be6abbfd112" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "unicode-bidi", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "log", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_adapters" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" +dependencies = [ + "icu_locid", + "icu_locid_transform", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "icu_segmenter" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de" +dependencies = [ + "core_maths", + "displaydoc", + "icu_collections", + "icu_locid", + "icu_provider", + "icu_segmenter_data", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_segmenter_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e52775179941363cc594e49ce99284d13d6948928d8e72c755f55e98caa1eb" + +[[package]] +name = "icu_timezone" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96" +dependencies = [ + "displaydoc", + "icu_calendar", + "icu_provider", + "icu_timezone_data", + "tinystr", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_timezone_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adcf7b613a268af025bc2a2532b4b9ee294e6051c5c0832d8bff20ac0232e68" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mozjs" +version = "0.14.1" +source = "git+https://github.com/servo/mozjs?rev=d90edd169aae1e16c1ef8b7bd4b2e0d93dda8ba2#d90edd169aae1e16c1ef8b7bd4b2e0d93dda8ba2" +dependencies = [ + "bindgen", + "cc", + "lazy_static", + "libc", + "log", + "mozjs_sys", +] + +[[package]] +name = "mozjs_sys" +version = "0.128.0-9" +source = "git+https://github.com/servo/mozjs?rev=d90edd169aae1e16c1ef8b7bd4b2e0d93dda8ba2#d90edd169aae1e16c1ef8b7bd4b2e0d93dda8ba2" +dependencies = [ + "bindgen", + "cc", + "encoding_c", + "encoding_c_mem", + "flate2", + "icu_capi", + "libc", + "libz-sys", + "tar", + "walkdir", +] + +[[package]] +name = "ndarray" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", +] + +[[package]] +name = "ndarray-stats" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400" +dependencies = [ + "indexmap", + "itertools 0.10.5", + "ndarray", + "noisy_float", + "num-integer", + "num-traits", + "rand", +] + +[[package]] +name = "noisy_float" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16843be85dd410c6a12251c4eca0dd1d3ee8c5725f746c4d5e0fdcec0a864b2" +dependencies = [ + "num-traits", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.1" +dependencies = [ + "bincode", + "bitflags", + "getopts", + "hashbrown 0.15.5", + "memchr", + "pulldown-cmark-escape", + "regex", + "serde", + "serde_json", + "unicase", +] + +[[package]] +name = "pulldown-cmark-bench" +version = "0.0.0" +dependencies = [ + "criterion", + "pulldown-cmark", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" + +[[package]] +name = "pulldown-cmark-fuzz" +version = "0.0.0" +dependencies = [ + "anyhow", + "libfuzzer-sys", + "mozjs", + "once_cell", + "pretty_assertions", + "pulldown-cmark", + "quick-xml", + "urlencoding", +] + +[[package]] +name = "quick-xml" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.14", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782" +dependencies = [ + "memchr", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ "proc-macro2", "quote", "syn", @@ -452,26 +1748,151 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strck" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be91090ded9d8f979d9fe921777342d37e769e0b6b7296843a7a38247240e917" + +[[package]] +name = "strck_ident" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c3802b169b3858a44667f221c9a0b3136e6019936ea926fc97fbad8af77202" +dependencies = [ + "strck", + "unicode-ident", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" -version = "2.0.63" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -484,30 +1905,57 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "version_check" -version = "0.9.4" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "walkdir" @@ -520,35 +1968,38 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" +name = "wasm-bindgen" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" dependencies = [ - "bumpalo", - "log", + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -556,111 +2007,348 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-sys", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows-targets", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +dependencies = [ + "either", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 310b59a4..8701691e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,12 @@ [workspace] -members = ["bench", "pulldown-cmark", "pulldown-cmark-escape"] +default-members = ["pulldown-cmark", "pulldown-cmark-escape"] +members = [ + "bench", + "dos-fuzzer", + "fuzz", + "pulldown-cmark", + "pulldown-cmark-escape", +] resolver = "2" [profile.release] diff --git a/README.md b/README.md index 85a808e8..6052da87 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,17 @@ a library. It is designed to be: -* Fast; a bare minimum of allocation and copying -* Safe; written in pure Rust with no unsafe blocks (except in the opt-in SIMD feature) -* Versatile; in particular source-maps are supported -* Correct; the goal is 100% compliance with the [CommonMark spec](http://spec.commonmark.org/) +- Fast; a bare minimum of allocation and copying +- Safe; written in pure Rust with no unsafe blocks (except in the opt-in SIMD feature) +- Versatile; in particular source-maps are supported +- Correct; the goal is 100% compliance with the [CommonMark spec](http://spec.commonmark.org/) Further, it optionally supports parsing footnotes, [Github flavored tables](https://github.github.com/gfm/#tables-extension-), -[Github flavored task lists](https://github.github.com/gfm/#task-list-items-extension-) and -[strikethrough](https://github.github.com/gfm/#strikethrough-extension-). +[Github flavored task lists](https://github.github.com/gfm/#task-list-items-extension-), +[strikethrough](https://github.github.com/gfm/#strikethrough-extension-) and +[highlight](https://github.com/markdown-it/markdown-it-mark) (`==marked==`, +rendered as ``). Rustc 1.71.1 or newer is required to build the crate. @@ -184,6 +186,31 @@ codegen-units = 1 panic = "abort" ``` +### `no_std` support + +`no_std` support can be enabled by compiling with `--no-default-features` to +disable `std` support and `--features hashbrown` for `Hash` collections that are only +defined in `std` for internal usages in crate. For example: + +```toml +[dependencies] +pulldown-cmark = { version = "*", default-features = false, features = ["hashbrown", "other features"] } +``` + +To support both `std` and `no_std` builds in project, you can use the following +in your `Cargo.toml`: + +```toml +[features] +default = ["std", "other features"] + +std = ["pulldown-cmark/std"] +hashbrown = ["pulldown-cmark/hashbrown"] +other_features = [] +[dependencies] +pulldown-cmark = { version = "*", default-features = false } +``` + ## Authors The main author is Raph Levien. The implementation of the new design (v0.3+) was diff --git a/bench/Cargo.toml b/bench/Cargo.toml index 21938218..13be69d1 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -19,5 +19,8 @@ harness = false [dependencies] pulldown-cmark = { path = "../pulldown-cmark" } +[features] +simd = [ "pulldown-cmark/simd" ] + [dev-dependencies] criterion = "0.5.1" diff --git a/bench/benches/html_rendering.rs b/bench/benches/html_rendering.rs index 2f30c3f5..788866eb 100644 --- a/bench/benches/html_rendering.rs +++ b/bench/benches/html_rendering.rs @@ -2,7 +2,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use pulldown_cmark::{html, Options, Parser}; use std::str::from_utf8; -static CRDT_BYTES: &[u8] = include_bytes!("../../pulldown-cmark/third_party/xi-editor/crdt.md"); +static CRDT_BYTES: &[u8] = include_bytes!("../third_party/xi-editor/crdt.md"); fn criterion_benchmark(c: &mut Criterion) { let mut full_opts = Options::empty(); @@ -91,6 +91,16 @@ This is a [link](example.com). **Cool!** b.iter(|| Parser::new_ext(input, Options::empty()).count()); }); + + c.bench_function("inline_link_to_sample", |b| { + let input = r###" + [Playground](https://play.rust-lang.org/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++let+mut+x+=+Some(42);%0A++++%0A++++let+prev+=+x.take_if(%7Cv%7C+if+*v+==+42+%7B%0A++++++++*v+%2B=+1;%0A++++++++false%0A++++%7D+else+%7B%0A++++++++false%0A++++%7D);%0A++++assert_eq!(x,+Some(43));%0A++++assert_eq!(prev,+None);%0A++++%0A++++let+prev+=+x.take_if(%7Cv%7C+*v+==+43);%0A++++assert_eq!(x,+None);%0A++++assert_eq!(prev,+Some(43));%0A%7D&edition=2021) + [Playground](https://play.rust-lang.org/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++let+mut+vec+=+Vec::new();%0A++++vec.push(1);%0A++++vec.push(2);%0A++++%0A++++assert_eq!(vec.len(),+2);%0A++++assert_eq!(vec%5B0%5D,+1);%0A++++%0A++++assert_eq!(vec.pop(),+Some(2));%0A++++assert_eq!(vec.len(),+1);%0A++++%0A++++vec%5B0%5D+=+7;%0A++++assert_eq!(vec%5B0%5D,+7);%0A++++%0A++++vec.extend(%5B1,+2,+3%5D);%0A++++%0A++++for+x+in+%26vec+%7B%0A++++++++println!(%22%7Bx%7D%22);%0A++++%7D%0A++++assert_eq!(vec,+%5B7,+1,+2,+3%5D);%0A%7D&edition=2021) + [Playground](https://play.rust-lang.org/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++let+mut+vec+=+Vec::with_capacity(10);%0A++++%0A++++//+The+vector+contains+no+items,+even+though+it+has+capacity+for+more%0A++++assert_eq!(vec.len(),+0);%0A++++assert!(vec.capacity()+%3E=+10);%0A++++%0A++++//+These+are+all+done+without+reallocating...%0A++++for+i+in+0..10+%7B%0A++++++++vec.push(i);%0A++++%7D%0A++++assert_eq!(vec.len(),+10);%0A++++assert!(vec.capacity()+%3E=+10);%0A++++%0A++++//+...but+this+may+make+the+vector+reallocate%0A++++vec.push(11);%0A++++assert_eq!(vec.len(),+11);%0A++++assert!(vec.capacity()+%3E=+11);%0A++++%0A++++//+A+vector+of+a+zero-sized+type+will+always+over-allocate,+since+no%0A++++//+allocation+is+necessary%0A++++let+vec_units+=+Vec::%3C()%3E::with_capacity(10);%0A++++assert_eq!(vec_units.capacity(),+usize::MAX);%0A%7D&edition=2021) + "###; + + b.iter(|| Parser::new_ext(input, Options::empty()).count()); + }); } criterion_group!(benches, criterion_benchmark); diff --git a/bench/benches/lib.rs b/bench/benches/lib.rs index 854d5e3a..288af791 100644 --- a/bench/benches/lib.rs +++ b/bench/benches/lib.rs @@ -5,8 +5,10 @@ mod to_html { use pulldown_cmark::{html, Options, Parser}; pub fn pathological_missing_table_cells(c: &mut Criterion) { - let mut group = c.benchmark_group(" pub fn pathological_missing_table_cells(c: &mut Criterion) { - "); + let mut group = c.benchmark_group( + " pub fn pathological_missing_table_cells(c: &mut Criterion) { + ", + ); let mut buf = String::new(); for i in 1..20 { buf.clear(); @@ -24,8 +26,10 @@ mod to_html { } pub fn pathological_link_def(c: &mut Criterion) { - let mut group = c.benchmark_group(" pub fn pathological_link_def(c: &mut Criterion) { - "); + let mut group = c.benchmark_group( + " pub fn pathological_link_def(c: &mut Criterion) { + ", + ); let mut buf = String::new(); for i in 1..20 { buf.clear(); diff --git a/bench/src/lib.rs b/bench/src/lib.rs index e69de29b..8b137891 100644 --- a/bench/src/lib.rs +++ b/bench/src/lib.rs @@ -0,0 +1 @@ + diff --git a/pulldown-cmark/third_party/markdown-it/LICENSE b/bench/third_party/markdown-it/LICENSE similarity index 100% rename from pulldown-cmark/third_party/markdown-it/LICENSE rename to bench/third_party/markdown-it/LICENSE diff --git a/pulldown-cmark/third_party/markdown-it/README.md b/bench/third_party/markdown-it/README.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/README.md rename to bench/third_party/markdown-it/README.md diff --git a/pulldown-cmark/third_party/markdown-it/block-bq-flat.md b/bench/third_party/markdown-it/block-bq-flat.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-bq-flat.md rename to bench/third_party/markdown-it/block-bq-flat.md diff --git a/pulldown-cmark/third_party/markdown-it/block-bq-nested.md b/bench/third_party/markdown-it/block-bq-nested.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-bq-nested.md rename to bench/third_party/markdown-it/block-bq-nested.md diff --git a/pulldown-cmark/third_party/markdown-it/block-code.md b/bench/third_party/markdown-it/block-code.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-code.md rename to bench/third_party/markdown-it/block-code.md diff --git a/pulldown-cmark/third_party/markdown-it/block-fences.md b/bench/third_party/markdown-it/block-fences.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-fences.md rename to bench/third_party/markdown-it/block-fences.md diff --git a/pulldown-cmark/third_party/markdown-it/block-heading.md b/bench/third_party/markdown-it/block-heading.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-heading.md rename to bench/third_party/markdown-it/block-heading.md diff --git a/pulldown-cmark/third_party/markdown-it/block-hr.md b/bench/third_party/markdown-it/block-hr.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-hr.md rename to bench/third_party/markdown-it/block-hr.md diff --git a/pulldown-cmark/third_party/markdown-it/block-html.md b/bench/third_party/markdown-it/block-html.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-html.md rename to bench/third_party/markdown-it/block-html.md diff --git a/pulldown-cmark/third_party/markdown-it/block-lheading.md b/bench/third_party/markdown-it/block-lheading.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-lheading.md rename to bench/third_party/markdown-it/block-lheading.md diff --git a/pulldown-cmark/third_party/markdown-it/block-list-flat.md b/bench/third_party/markdown-it/block-list-flat.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-list-flat.md rename to bench/third_party/markdown-it/block-list-flat.md diff --git a/pulldown-cmark/third_party/markdown-it/block-list-nested.md b/bench/third_party/markdown-it/block-list-nested.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-list-nested.md rename to bench/third_party/markdown-it/block-list-nested.md diff --git a/pulldown-cmark/third_party/markdown-it/block-ref-flat.md b/bench/third_party/markdown-it/block-ref-flat.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-ref-flat.md rename to bench/third_party/markdown-it/block-ref-flat.md diff --git a/pulldown-cmark/third_party/markdown-it/block-ref-nested.md b/bench/third_party/markdown-it/block-ref-nested.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/block-ref-nested.md rename to bench/third_party/markdown-it/block-ref-nested.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-autolink.md b/bench/third_party/markdown-it/inline-autolink.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-autolink.md rename to bench/third_party/markdown-it/inline-autolink.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-backticks.md b/bench/third_party/markdown-it/inline-backticks.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-backticks.md rename to bench/third_party/markdown-it/inline-backticks.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-em-flat.md b/bench/third_party/markdown-it/inline-em-flat.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-em-flat.md rename to bench/third_party/markdown-it/inline-em-flat.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-em-nested.md b/bench/third_party/markdown-it/inline-em-nested.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-em-nested.md rename to bench/third_party/markdown-it/inline-em-nested.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-em-worst.md b/bench/third_party/markdown-it/inline-em-worst.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-em-worst.md rename to bench/third_party/markdown-it/inline-em-worst.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-entity.md b/bench/third_party/markdown-it/inline-entity.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-entity.md rename to bench/third_party/markdown-it/inline-entity.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-escape.md b/bench/third_party/markdown-it/inline-escape.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-escape.md rename to bench/third_party/markdown-it/inline-escape.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-html.md b/bench/third_party/markdown-it/inline-html.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-html.md rename to bench/third_party/markdown-it/inline-html.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-links-flat.md b/bench/third_party/markdown-it/inline-links-flat.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-links-flat.md rename to bench/third_party/markdown-it/inline-links-flat.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-links-nested.md b/bench/third_party/markdown-it/inline-links-nested.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-links-nested.md rename to bench/third_party/markdown-it/inline-links-nested.md diff --git a/pulldown-cmark/third_party/markdown-it/inline-newlines.md b/bench/third_party/markdown-it/inline-newlines.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/inline-newlines.md rename to bench/third_party/markdown-it/inline-newlines.md diff --git a/pulldown-cmark/third_party/markdown-it/lorem1.md b/bench/third_party/markdown-it/lorem1.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/lorem1.md rename to bench/third_party/markdown-it/lorem1.md diff --git a/pulldown-cmark/third_party/markdown-it/rawtabs.md b/bench/third_party/markdown-it/rawtabs.md similarity index 100% rename from pulldown-cmark/third_party/markdown-it/rawtabs.md rename to bench/third_party/markdown-it/rawtabs.md diff --git a/pulldown-cmark/third_party/xi-editor/LICENSE b/bench/third_party/xi-editor/LICENSE similarity index 100% rename from pulldown-cmark/third_party/xi-editor/LICENSE rename to bench/third_party/xi-editor/LICENSE diff --git a/pulldown-cmark/third_party/xi-editor/crdt.md b/bench/third_party/xi-editor/crdt.md similarity index 100% rename from pulldown-cmark/third_party/xi-editor/crdt.md rename to bench/third_party/xi-editor/crdt.md diff --git a/fuzzer/.gitignore b/dos-fuzzer/.gitignore similarity index 100% rename from fuzzer/.gitignore rename to dos-fuzzer/.gitignore diff --git a/fuzzer/Cargo.toml b/dos-fuzzer/Cargo.toml similarity index 50% rename from fuzzer/Cargo.toml rename to dos-fuzzer/Cargo.toml index ffa76435..498e4833 100644 --- a/fuzzer/Cargo.toml +++ b/dos-fuzzer/Cargo.toml @@ -1,29 +1,22 @@ [package] -name = "fuzzer" +name = "dos-fuzzer" version = "0.1.0" authors = ["oberien "] +publish = false edition = "2018" [dependencies] -syn = { version = "0.15", features = ["full"] } -itertools = "0.8" -walkdir = "2.2" +syn = { version = "2", features = ["full", "visit"] } +itertools = "0.10" +walkdir = "2.5" pulldown-cmark = { path = "../pulldown-cmark" } -ndarray = "0.12" -ndarray-stats = "0.2" -rand = "0.7" -rand_xoshiro = "0.4" -num_cpus = "1.10" +ndarray = "0.15" +ndarray-stats = "0.5" +rand = "0.8" +rand_xoshiro = "0.6" +num_cpus = "1.16" crossbeam-utils = "0.8" libc = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" clap = { version = "4.3", features = ["derive"] } - -[profile.release] -lto = "fat" - -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - diff --git a/fuzzer/README.md b/dos-fuzzer/README.md similarity index 94% rename from fuzzer/README.md rename to dos-fuzzer/README.md index ed60879a..14ac9fce 100644 --- a/fuzzer/README.md +++ b/dos-fuzzer/README.md @@ -1,6 +1,9 @@ # Fuzzer for detecting superlinear growth in pulldown-cmark This fuzzer tries to find superlinear growth in pulldown-cmark wrt. input length. +Patterns that exhibit superlinear (e.g. quadratic) growth can potentially be +used for denial-of-service attacks, or cause slow parsing even in normal use. + The general approach is to parse the source code of pulldown-cmark, extract literals which are used in branching code (if-conditions, match patterns, match guards, …) and add some manually. diff --git a/fuzzer/group-panics b/dos-fuzzer/group-panics similarity index 100% rename from fuzzer/group-panics rename to dos-fuzzer/group-panics diff --git a/fuzzer/plot b/dos-fuzzer/plot similarity index 100% rename from fuzzer/plot rename to dos-fuzzer/plot diff --git a/fuzzer/retest-output b/dos-fuzzer/retest-output similarity index 100% rename from fuzzer/retest-output rename to dos-fuzzer/retest-output diff --git a/dos-fuzzer/run b/dos-fuzzer/run new file mode 100755 index 00000000..8ed3faaf --- /dev/null +++ b/dos-fuzzer/run @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +cargo rustc --release -- -C target-cpu=native +sudo nice -n -20 env RUST_BACKTRACE=1 ../target/release/dos-fuzzer 2>&1 | tee -a output diff --git a/fuzzer/src/black_box.rs b/dos-fuzzer/src/black_box.rs similarity index 100% rename from fuzzer/src/black_box.rs rename to dos-fuzzer/src/black_box.rs diff --git a/fuzzer/src/clock.rs b/dos-fuzzer/src/clock.rs similarity index 100% rename from fuzzer/src/clock.rs rename to dos-fuzzer/src/clock.rs diff --git a/dos-fuzzer/src/literals.rs b/dos-fuzzer/src/literals.rs new file mode 100644 index 00000000..66464c2d --- /dev/null +++ b/dos-fuzzer/src/literals.rs @@ -0,0 +1,179 @@ +use std::collections::HashSet; +use std::fs; +use std::path::Path; + +use syn::{visit::Visit, Lit}; +use walkdir::WalkDir; + +/// Get all relevant literals from pulldown-cmark to generate fuzzing input from. +/// +/// This method iterates over the source code of pulldown-cmark (except for `entities.rs` and `main.rs`. +/// It parses the source code an extracts all literals used in consts, statics, conditions and match +/// arm patterns and guards. Literals are Strs, ByteStrs, chars and bytes. +/// If an array is encountered, only the first element is extracted. We assume that all elements in +/// the array are used the same way to enter the same branch, like `if ["foo", "bar"].contains("baz")`. +/// +/// Additionally, it manually adds some literals for uncovered edge-cases. +pub fn get() -> Vec> { + // Get relevant literals from pulldown-cmark's source code. + // See documentaiton of `literals`-module for more information. + let walkdir = WalkDir::new("../pulldown-cmark/src") + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .filter(|e| { + if let Some(ext) = e.path().extension() { + ext == "rs" + } else { + false + } + }); + + let mut literal_parser = LiteralParser::new(); + + let skipped_files = &[ + Path::new("../pulldown-cmark/src/entities.rs"), + Path::new("../pulldown-cmark/src/main.rs"), + ]; + for file in walkdir { + if skipped_files.contains(&file.path()) { + continue; + } + literal_parser.extract_literals_from_file(file.path()); + } + + let mut literals = literal_parser.into_literals(); + + // Manually add literals. + // ensure that the branch calling into `entities.rs` is covered + literals.insert("&".into()); + // Unicode no-break space + literals.insert("\u{a0}".into()); + // 1/2/3/4-byte UTF-8 characters to ensure correct handling of unicode + literals.insert("\u{0}".into()); + literals.insert("\u{80}".into()); + literals.insert("\u{800}".into()); + literals.insert("\u{10000}".into()); + + literals + .into_iter() + .filter(|lit| !lit.contains(&b'\n') || lit.len() == 1) + .filter(|lit| !lit.is_empty()) + .collect() +} + +#[derive(Debug, Default)] +struct LiteralParser { + literals: HashSet>, + // Whenever we enter an if-condition / match arm pattern, we increment this value. + // Whenever we leave it again, we decrement it. This ensures correct behaviour when having a + // condition inside a condition (which is to just extract everything). + condition_depth: u32, + in_static: bool, + in_const: bool, +} + +impl LiteralParser { + pub fn new() -> LiteralParser { + Default::default() + } + + pub fn into_literals(self) -> HashSet> { + assert_eq!(self.condition_depth, 0); + self.literals + } + + pub fn extract_literals_from_file>(&mut self, path: P) { + let path = path.as_ref(); + let content = fs::read_to_string(path).expect(&format!("unable to read file {:?}", path)); + let parsed = syn::parse_file(&content).expect(&format!("unable to parse file {:?}", path)); + self.visit_file(&parsed); + } +} + +impl<'ast> Visit<'ast> for LiteralParser { + fn visit_lit(&mut self, lit: &'ast Lit) { + if self.condition_depth == 0 && !self.in_static && !self.in_const { + return; + } + match lit { + Lit::Str(s) => drop(self.literals.insert(s.value().into_bytes())), + Lit::ByteStr(s) => drop(self.literals.insert(s.value())), + Lit::CStr(s) => drop(self.literals.insert(s.value().into_bytes())), + Lit::Byte(b) => drop(self.literals.insert(vec![b.value()])), + Lit::Char(c) => { + let c = c.value(); + let mut v = vec![0; c.len_utf8()]; + c.encode_utf8(&mut v); + self.literals.insert(v); + } + _ => (), + } + } + + fn visit_item_static(&mut self, item: &'ast syn::ItemStatic) { + self.in_static = true; + syn::visit::visit_item_static(self, item); + self.in_static = false; + } + + fn visit_item_const(&mut self, item: &'ast syn::ItemConst) { + self.in_const = true; + syn::visit::visit_item_const(self, item); + self.in_const = false; + } + + fn visit_expr_if(&mut self, expr: &'ast syn::ExprIf) { + self.condition_depth += 1; + self.visit_expr(&*expr.cond); + self.condition_depth -= 1; + self.visit_block(&expr.then_branch); + if let Some((_, branch)) = &expr.else_branch { + self.visit_expr(&*branch); + } + } + + fn visit_expr_while(&mut self, expr: &'ast syn::ExprWhile) { + self.condition_depth += 1; + self.visit_expr(&*expr.cond); + self.condition_depth -= 1; + self.visit_block(&expr.body); + } + + fn visit_expr_for_loop(&mut self, expr: &'ast syn::ExprForLoop) { + self.condition_depth += 1; + self.visit_expr(&*expr.expr); + self.condition_depth -= 1; + self.visit_block(&expr.body); + } + + fn visit_expr_match(&mut self, expr: &'ast syn::ExprMatch) { + self.condition_depth += 1; + self.visit_expr(&*expr.expr); + self.condition_depth -= 1; + for it in &expr.arms { + self.visit_arm(it); + } + } + + fn visit_arm(&mut self, arm: &'ast syn::Arm) { + self.visit_pat(&arm.pat); + self.condition_depth += 1; + if let Some((_, guard)) = &arm.guard { + self.visit_expr(guard); + } + self.condition_depth -= 1; + self.visit_expr(&*arm.body); + } + + fn visit_pat(&mut self, pat: &'ast syn::Pat) { + // Covers let-else patterns etc. + self.condition_depth += 1; + syn::visit::visit_pat(self, pat); + self.condition_depth -= 1; + } + + fn visit_attribute(&mut self, _attr: &'ast syn::Attribute) { + // Ignore attributes, e.g. doc comments + } +} diff --git a/fuzzer/src/main.rs b/dos-fuzzer/src/main.rs similarity index 98% rename from fuzzer/src/main.rs rename to dos-fuzzer/src/main.rs index 5d67c466..6870598d 100644 --- a/fuzzer/src/main.rs +++ b/dos-fuzzer/src/main.rs @@ -227,6 +227,8 @@ fn regression_test() -> i32 { check_pattern("a***".into()); check_pattern("[[]()".into()); check_pattern("[a](<".into()); + // https://github.com/pulldown-cmark/pulldown-cmark/issues/934 + check_pattern("!-- <".into()); exit_code } @@ -277,7 +279,7 @@ fn fuzz(num_cpus: usize) { serde_json::to_string(&pattern).unwrap(), ); let args: Vec<_> = env::args().collect(); - Command::new(&args[0]).args(&args[1..]).exec(); + let _ = Command::new(&args[0]).args(&args[1..]).exec(); unreachable!(); } } diff --git a/fuzzer/src/scoring.rs b/dos-fuzzer/src/scoring.rs similarity index 97% rename from fuzzer/src/scoring.rs rename to dos-fuzzer/src/scoring.rs index d1e74a38..48d1eb3c 100644 --- a/fuzzer/src/scoring.rs +++ b/dos-fuzzer/src/scoring.rs @@ -12,7 +12,9 @@ pub fn pearson_correlation(time_samples: &[(f64, f64)]) -> (f64, bool) { vec.extend(time_samples.iter().cloned().map(|(x, y)| [x, y])); let time_samples = Array2::from(vec); let time_samples = time_samples.t(); - let corr = time_samples.pearson_correlation()[[1, 0]]; + let corr = time_samples + .pearson_correlation() + .expect("no time samples given")[[1, 0]]; (corr, corr < super::ACCEPTANCE_CORRELATION) } diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock deleted file mode 100644 index 7f56b3f7..00000000 --- a/fuzz/Cargo.lock +++ /dev/null @@ -1,699 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bindgen" -version = "0.69.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", -] - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "encoding_c" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af727805f3b0d79956bde5b35732669fb5c5d45a94893798e7b7e70cfbf9cc1" -dependencies = [ - "encoding_rs", -] - -[[package]] -name = "encoding_c_mem" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a80a16821fe8c7cab96e0c67b57cd7090e021e9615e6ce6ab0cf866c44ed1f0" -dependencies = [ - "encoding_rs", -] - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - -[[package]] -name = "libloading" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "libz-sys" -version = "1.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "mozjs" -version = "0.14.1" -source = "git+https://github.com/notriddle/mozjs#19e9d3b52013e301e433c5580ef49d874f1bbf92" -dependencies = [ - "bindgen", - "cc", - "lazy_static", - "libc", - "log", - "mozjs_sys", - "num-traits", -] - -[[package]] -name = "mozjs_sys" -version = "0.68.2" -source = "git+https://github.com/notriddle/mozjs#19e9d3b52013e301e433c5580ef49d874f1bbf92" -dependencies = [ - "bindgen", - "cc", - "encoding_c", - "encoding_c_mem", - "libc", - "libz-sys", - "walkdir", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pkg-config" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" - -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] - -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pulldown-cmark" -version = "0.11.0" -dependencies = [ - "bitflags", - "getopts", - "memchr", - "pulldown-cmark-escape", - "unicase", -] - -[[package]] -name = "pulldown-cmark-escape" -version = "0.11.0" - -[[package]] -name = "pulldown-cmark-fuzz" -version = "0.0.0" -dependencies = [ - "anyhow", - "libfuzzer-sys", - "mozjs", - "once_cell", - "pretty_assertions", - "pulldown-cmark", - "quick-xml", - "urlencoding", -] - -[[package]] -name = "quick-xml" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "syn" -version = "2.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index fb2c146c..f53a8778 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -14,15 +14,16 @@ libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } once_cell = "1.18.0" pretty_assertions = "1.3.0" quick-xml = "0.29" -mozjs = { git = "https://github.com/notriddle/mozjs", features = ["streams"] } +mozjs = { git = "https://github.com/servo/mozjs", rev = "d90edd169aae1e16c1ef8b7bd4b2e0d93dda8ba2", features = [ + "streams", +] } urlencoding = "2.1.2" [dependencies.pulldown-cmark] path = "../pulldown-cmark" -# Prevent this from interfering with workspaces -[workspace] -members = ["."] +[features] +simd = [ "pulldown-cmark/simd" ] [[bin]] name = "parse" diff --git a/fuzz/README.md b/fuzz/README.md new file mode 100644 index 00000000..21a17c0d --- /dev/null +++ b/fuzz/README.md @@ -0,0 +1,25 @@ +# Fuzz targets + +This crate specifies fuzzing targets which are +instrumented with [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz). + +## Fixing fuzz build issue + +At the moment, building fuzz targets with default settings +(`cargo fuzz build`) throws many errors like this: + +``` +: rust-lld: error: undefined symbol: __sancov_gen_.1094 + >>> referenced by parse.3be71763e9de75d0-cgu.0 + >>> /home/user/projects/pulldown-cmark/target/x86_64-unknown-linux-gnu/release/deps/parse-4bac226fcf249aac.parse.3be71763e9de75d0-cgu.0.rcgu.o:(asan.module_dtor.1168) +``` + +The issue seems to be triggered during linking of the binaries +with default `profile.release.lto=true` set at the workspace `Cargo.toml` +file. + +To fix the build, you can override `lto` config using env variable: + +```bash +$ CARGO_PROFILE_RELEASE_LTO=thin cargo fuzz run parse -- -only_ascii=1 -max_total_time=60 +``` diff --git a/fuzz/fuzz_targets/parse.rs b/fuzz/fuzz_targets/parse.rs index 6f3a7c59..71286a32 100644 --- a/fuzz/fuzz_targets/parse.rs +++ b/fuzz/fuzz_targets/parse.rs @@ -2,60 +2,15 @@ use libfuzzer_sys::fuzz_target; use libfuzzer_sys::arbitrary::{self, Arbitrary}; -use pulldown_cmark::Options; #[derive(Debug, Arbitrary)] struct FuzzingInput<'a> { + options: u32, markdown: &'a str, - tables: bool, - footnotes: bool, - strikethrough: bool, - tasklists: bool, - smart_punctuation: bool, - heading_attributes: bool, - metadata_block: bool, - math: bool, - gfm: bool, } fuzz_target!(|data: FuzzingInput<'_>| { - let mut opts = pulldown_cmark::Options::empty(); - - if data.tables { - opts.insert(Options::ENABLE_TABLES); - } - - if data.footnotes { - opts.insert(Options::ENABLE_FOOTNOTES); - } - - if data.strikethrough { - opts.insert(Options::ENABLE_STRIKETHROUGH); - } - - if data.tasklists { - opts.insert(Options::ENABLE_TASKLISTS); - } - - if data.smart_punctuation { - opts.insert(Options::ENABLE_SMART_PUNCTUATION); - } - - if data.heading_attributes { - opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); - } - - if data.metadata_block { - opts.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS); - } - - if data.math { - opts.insert(Options::ENABLE_MATH); - } - - if data.gfm { - opts.insert(Options::ENABLE_GFM); - } + let opts = pulldown_cmark::Options::from_bits_truncate(data.options); for _ in pulldown_cmark::Parser::new_ext(data.markdown, opts) {} }); diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 9981ff45..adea8a9b 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -4,13 +4,15 @@ use std::convert::TryInto; use std::ptr; use anyhow::anyhow; -use mozjs::jsapi::{EnterRealm, HandleValueArray, LeaveRealm, JS_NewGlobalObject, OnNewGlobalHookOption}; +use mozjs::conversions::ToJSValConvertible; +use mozjs::jsapi::{ + EnterRealm, HandleValueArray, JS_NewGlobalObject, LeaveRealm, OnNewGlobalHookOption, +}; use mozjs::jsval::UndefinedValue; use mozjs::rooted; +use mozjs::rust::wrappers::JS_CallFunctionName; use mozjs::rust::SIMPLE_GLOBAL_CLASS; use mozjs::rust::{JSEngine, RealmOptions, Runtime}; -use mozjs::rust::wrappers::JS_CallFunctionName; -use mozjs::conversions::ToJSValConvertible; use pulldown_cmark::{CodeBlockKind, Event, LinkType, Parser, Tag, TagEnd}; use quick_xml::escape::unescape; use quick_xml::events::Event as XmlEvent; @@ -56,7 +58,13 @@ pub fn commonmark_js(text: &str) -> anyhow::Result { // These should indicate source location for diagnostics. let filename: &'static str = "commonmark.min.js"; let lineno: u32 = 1; - let res = rt.evaluate_script(global.handle(), COMMONMARK_MIN_JS, filename, lineno, rval.handle_mut()); + let res = rt.evaluate_script( + global.handle(), + COMMONMARK_MIN_JS, + filename, + lineno, + rval.handle_mut(), + ); assert!(res.is_ok()); let filename: &'static str = "{inline}"; @@ -69,7 +77,13 @@ pub fn commonmark_js(text: &str) -> anyhow::Result { } "#; rooted!(in(rt.cx()) let mut render_to_xml = UndefinedValue()); - let res = rt.evaluate_script(global.handle(), script, filename, lineno, render_to_xml.handle_mut()); + let res = rt.evaluate_script( + global.handle(), + script, + filename, + lineno, + render_to_xml.handle_mut(), + ); assert!(res.is_ok()); // rval now contains a reference to the render_to_xml function @@ -89,14 +103,16 @@ pub fn commonmark_js(text: &str) -> anyhow::Result { utf8 }; - unsafe { LeaveRealm(rt.cx(), realm); } + unsafe { + LeaveRealm(rt.cx(), realm); + } Ok(xml) }) } /// Parse commonmark.js XML and return Markdown events. -pub fn xml_to_events(xml: &str) -> anyhow::Result> { +pub fn xml_to_events(xml: &str) -> anyhow::Result>> { let mut block_container_stack = Vec::new(); let mut heading_stack = Vec::new(); @@ -108,7 +124,12 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { XmlEvent::Decl(..) | XmlEvent::DocType(..) => continue, XmlEvent::Start(tag) => match tag.name().as_ref() { b"document" => continue, - b"paragraph" if block_container_stack.last().map(|(_start, tight)| *tight).unwrap_or(false) => { + b"paragraph" + if block_container_stack + .last() + .map(|(_start, tight)| *tight) + .unwrap_or(false) => + { continue; } b"paragraph" => events.push(Event::Start(Tag::Paragraph)), @@ -159,9 +180,7 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { None => events.push(Event::Start(Tag::List(None))), }; let tight = match tag.try_get_attribute("tight") { - Ok(Some(value)) if value.unescape_value()? == "true" => { - true - } + Ok(Some(value)) if value.unescape_value()? == "true" => true, _ => false, }; block_container_stack.push((start.is_some(), tight)); @@ -206,7 +225,7 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { b"block_quote" => { block_container_stack.push((true, false)); events.push(Event::Start(Tag::BlockQuote(None))) - }, + } b"html_block" => { events.push(Event::Start(Tag::HtmlBlock)); events.push(Event::Html( @@ -215,7 +234,7 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { .into(), )); events.push(Event::End(TagEnd::HtmlBlock)); - }, + } b"html_inline" => events.push(Event::InlineHtml( unescape(&reader.read_text(tag.to_end().name())?)? .into_owned() @@ -225,7 +244,12 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { }, XmlEvent::End(tag) => match tag.name().as_ref() { b"document" => continue, - b"paragraph" if block_container_stack.last().map(|(_numbered, tight)| *tight).unwrap_or(false) => { + b"paragraph" + if block_container_stack + .last() + .map(|(_numbered, tight)| *tight) + .unwrap_or(false) => + { continue; } b"paragraph" => events.push(Event::End(TagEnd::Paragraph)), @@ -233,7 +257,10 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { heading_stack.pop().ok_or(anyhow!("Heading stack empty"))?, ))), b"list" => events.push(Event::End(TagEnd::List( - block_container_stack.pop().ok_or(anyhow!("List stack empty"))?.0, + block_container_stack + .pop() + .ok_or(anyhow!("List stack empty"))? + .0, ))), b"item" => events.push(Event::End(TagEnd::Item)), b"emph" => events.push(Event::End(TagEnd::Emphasis)), @@ -241,9 +268,11 @@ pub fn xml_to_events(xml: &str) -> anyhow::Result> { b"link" => events.push(Event::End(TagEnd::Link)), b"image" => events.push(Event::End(TagEnd::Image)), b"block_quote" => { - block_container_stack.pop().ok_or(anyhow!("List stack empty"))?; - events.push(Event::End(TagEnd::BlockQuote)) - }, + block_container_stack + .pop() + .ok_or(anyhow!("List stack empty"))?; + events.push(Event::End(TagEnd::BlockQuote(None))) + } name => anyhow::bail!("end tag: {}", String::from_utf8_lossy(name)), }, XmlEvent::Text(_) => continue, @@ -323,9 +352,7 @@ pub fn normalize(events: Vec>) -> Vec> { id: "".into(), // commonmark.js does not record this })), Event::Start(Tag::Link { - dest_url, - title, - .. + dest_url, title, .. }) => Some(Event::Start(Tag::Link { link_type: LinkType::Inline, dest_url: urldecode(&dest_url).into(), diff --git a/fuzzer/Cargo.lock b/fuzzer/Cargo.lock deleted file mode 100644 index 8dbb2cc8..00000000 --- a/fuzzer/Cargo.lock +++ /dev/null @@ -1,799 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anstream" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuzzer" -version = "0.1.0" -dependencies = [ - "clap", - "crossbeam-utils", - "itertools 0.8.2", - "libc", - "ndarray", - "ndarray-stats", - "num_cpus", - "pulldown-cmark", - "rand 0.7.3", - "rand_xoshiro", - "serde", - "serde_json", - "syn 0.15.44", - "walkdir", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg 1.1.0", - "hashbrown", -] - -[[package]] -name = "itertools" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "matrixmultiply" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002" -dependencies = [ - "rawpointer", -] - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "ndarray" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf380a8af901ad627594013a3bbac903ae0a6f94e176e47e46b5bbc1877b928" -dependencies = [ - "itertools 0.7.11", - "matrixmultiply", - "num-complex", - "num-traits", -] - -[[package]] -name = "ndarray-stats" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4570b4f029fb2a8bd5f1f629753fb0b01c0cc8343f5eb77c9dc6699d9cf7b036" -dependencies = [ - "indexmap", - "itertools 0.8.2", - "ndarray", - "noisy_float", - "num-integer", - "num-traits", - "rand 0.6.5", -] - -[[package]] -name = "noisy_float" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deb89f8062c0dce4c805a987daff07f92e17c560f7c3ed631282555af902258" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg 1.1.0", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg 1.1.0", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pulldown-cmark" -version = "0.10.0" -dependencies = [ - "bitflags 2.4.2", - "getopts", - "memchr", - "pulldown-cmark-escape", - "unicase", -] - -[[package]] -name = "pulldown-cmark-escape" -version = "0.10.0" - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2 1.0.78", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_xoshiro" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rawpointer" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019" - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "serde" -version = "1.0.196" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.196" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "serde_json" -version = "1.0.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - -[[package]] -name = "syn" -version = "2.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "unicode-ident", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/fuzzer/run b/fuzzer/run deleted file mode 100755 index cd19ca44..00000000 --- a/fuzzer/run +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -cargo rustc --release -- -C target-cpu=native -sudo nice -n -20 env RUST_BACKTRACE=1 ./target/release/fuzzer 2>&1 | tee -a output diff --git a/fuzzer/src/literals.rs b/fuzzer/src/literals.rs deleted file mode 100644 index 0db656f8..00000000 --- a/fuzzer/src/literals.rs +++ /dev/null @@ -1,366 +0,0 @@ -use std::collections::HashSet; -use std::fs; -use std::path::Path; - -use syn::{Expr, ExprArray, ExprCall, ExprMethodCall, ExprTuple, ImplItem, Item, Lit, Pat, Stmt}; -use walkdir::WalkDir; - -/// Get all relevant literals from pulldown-cmark to generate fuzzing input from. -/// -/// This method iterates over the source code of pulldown-cmark (except for `entities.rs` and `main.rs`. -/// It parses the source code an extracts all literals used in consts, statics, conditions and match -/// arm patterns and guards. Literals are Strs, ByteStrs, chars and bytes. -/// If an array is encountered, only the first element is extracted. We assume that all elements in -/// the array are used the same way to enter the same branch, like `if ["foo", "bar"].contains("baz")`. -/// -/// Additionally, it manually adds some literals for uncovered edge-cases. -pub fn get() -> Vec> { - // Get relevant literals from pulldown-cmark's source code. - // See documentaiton of `literals`-module for more information. - let walkdir = WalkDir::new("../src") - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.file_type().is_file()) - .filter(|e| { - if let Some(ext) = e.path().extension() { - ext == "rs" - } else { - false - } - }); - - let mut literal_parser = LiteralParser::new(); - - let skipped_files = &[Path::new("../src/entities.rs"), Path::new("../src/main.rs")]; - for file in walkdir { - if skipped_files.contains(&file.path()) { - continue; - } - literal_parser.extract_literals_from_file(file.path()); - } - - let mut literals = literal_parser.into_literals(); - - // Manually add literals. - // ensure that the branch calling into `entities.rs` is covered - literals.insert("&".into()); - // Unicode no-break space - literals.insert("\u{a0}".into()); - // 1/2/3/4-byte UTF-8 characters to ensure correct handling of unicode - literals.insert("\u{0}".into()); - literals.insert("\u{80}".into()); - literals.insert("\u{800}".into()); - literals.insert("\u{10000}".into()); - - literals - .into_iter() - .filter(|lit| !lit.contains(&b'\n')) - .filter(|lit| !lit.is_empty()) - .collect() -} - -#[derive(Debug, Default)] -struct LiteralParser { - literals: HashSet>, - // Whenever we enter an if-condition / match arm pattern, we increment this value. - // Whenever we leave it again, we decrement it. This ensures correct behaviour when having a - // condition inside a condition (which is to just extract everything). - condition_depth: u32, - in_static: bool, - in_const: bool, -} - -impl LiteralParser { - pub fn new() -> LiteralParser { - Default::default() - } - - pub fn into_literals(self) -> HashSet> { - assert_eq!(self.condition_depth, 0); - self.literals - } - - pub fn extract_literals_from_file>(&mut self, path: P) { - let path = path.as_ref(); - let content = fs::read_to_string(path).expect(&format!("unable to read file {:?}", path)); - let parsed = syn::parse_file(&content).expect(&format!("unable to parse file {:?}", path)); - self.extract_literals_from_items(parsed.items); - } - - fn extract_literals_from_items(&mut self, items: Vec) { - for item in items { - self.extract_literals_from_item(item); - } - } - - fn extract_literals_from_item(&mut self, item: Item) { - match item { - Item::ExternCrate(_) - | Item::Use(_) - | Item::ForeignMod(_) - | Item::Type(_) - | Item::Existential(_) - | Item::Struct(_) - | Item::Enum(_) - | Item::Union(_) - | Item::Trait(_) - | Item::TraitAlias(_) - | Item::Verbatim(_) => (), - Item::Static(item) => { - self.in_static = true; - self.extract_literals_from_expr(*item.expr); - self.in_static = false; - } - Item::Const(item) => { - self.in_const = true; - self.extract_literals_from_expr(*item.expr); - self.in_const = false; - } - Item::Fn(item) => self.extract_literals_from_stmts(item.block.stmts), - Item::Mod(item) => { - if let Some((_, item)) = item.content { - self.extract_literals_from_items(item); - } - } - Item::Impl(item) => self.extract_literals_from_impl(item.items), - Item::Macro(_) => (), - Item::Macro2(_) => unimplemented!("macros 2.0"), - } - } - - fn extract_literals_from_stmts(&mut self, stmts: Vec) { - for stmt in stmts { - match stmt { - Stmt::Local(local) => { - if let Some((_, expr)) = local.init { - self.extract_literals_from_expr(*expr); - } - } - Stmt::Item(item) => self.extract_literals_from_item(item), - Stmt::Expr(expr) | Stmt::Semi(expr, _) => self.extract_literals_from_expr(expr), - } - } - } - - fn extract_literals_from_impl(&mut self, items: Vec) { - for item in items { - match item { - ImplItem::Const(item) => { - self.in_const = true; - self.extract_literals_from_expr(item.expr); - self.in_const = false; - } - ImplItem::Method(item) => self.extract_literals_from_stmts(item.block.stmts), - ImplItem::Type(_) | ImplItem::Existential(_) => (), - ImplItem::Macro(_) => (), - ImplItem::Verbatim(_) => unimplemented!("ImplItem::Verbatim"), - } - } - } - - fn extract_literals_from_lit(&mut self, lit: Lit) { - if self.condition_depth == 0 && !self.in_static && !self.in_const { - return; - } - match lit { - Lit::Str(s) => drop(self.literals.insert(s.value().into_bytes())), - Lit::ByteStr(s) => drop(self.literals.insert(s.value())), - Lit::Byte(b) => drop(self.literals.insert(vec![b.value()])), - Lit::Char(c) => { - let c = c.value(); - let mut v = vec![0; c.len_utf8()]; - c.encode_utf8(&mut v); - self.literals.insert(v); - } - Lit::Int(_) | Lit::Float(_) | Lit::Bool(_) | Lit::Verbatim(_) => (), - } - } - - fn extract_literals_from_expr(&mut self, expr: Expr) { - match expr { - Expr::Box(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::InPlace(expr) => self.extract_literals_from_expr(*expr.value), - Expr::Array(ExprArray { elems, .. }) => { - // If an array is used, we assume that all elements are equal. - // For example in `if ["foo", "bar"].contains(baz) {` it's enough to just extract the - // first element to cover that branch. - if let Some(elem) = elems.into_iter().next() { - self.extract_literals_from_expr(elem); - } - } - Expr::Tuple(ExprTuple { elems, .. }) => { - for expr in elems { - self.extract_literals_from_expr(expr); - } - } - Expr::Call(ExprCall { func, args, .. }) - | Expr::MethodCall(ExprMethodCall { - args, - receiver: func, - .. - }) => { - self.extract_literals_from_expr(*func); - for arg in args { - self.extract_literals_from_expr(arg); - } - } - Expr::Binary(expr) => { - self.extract_literals_from_expr(*expr.left); - self.extract_literals_from_expr(*expr.right); - } - Expr::Unary(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Lit(expr) => self.extract_literals_from_lit(expr.lit), - Expr::Cast(_) => (), - Expr::Type(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Let(expr) => { - self.condition_depth += 1; - for pat in expr.pats { - self.extract_literals_from_pat(pat); - } - self.condition_depth -= 1; - self.extract_literals_from_expr(*expr.expr) - } - Expr::If(expr) => { - self.condition_depth += 1; - self.extract_literals_from_expr(*expr.cond); - self.condition_depth -= 1; - self.extract_literals_from_stmts(expr.then_branch.stmts); - if let Some((_, expr)) = expr.else_branch { - self.extract_literals_from_expr(*expr) - } - } - Expr::While(expr) => { - self.condition_depth += 1; - self.extract_literals_from_expr(*expr.cond); - self.condition_depth -= 1; - self.extract_literals_from_stmts(expr.body.stmts); - } - Expr::ForLoop(expr) => { - self.condition_depth += 1; - self.extract_literals_from_expr(*expr.expr); - self.condition_depth -= 1; - self.extract_literals_from_stmts(expr.body.stmts); - } - Expr::Loop(expr) => self.extract_literals_from_stmts(expr.body.stmts), - Expr::Match(arm) => { - self.condition_depth += 1; - self.extract_literals_from_expr(*arm.expr); - self.condition_depth -= 1; - for arm in arm.arms { - self.condition_depth += 1; - for pat in arm.pats { - self.extract_literals_from_pat(pat); - } - if let Some((_, guard)) = arm.guard { - self.extract_literals_from_expr(*guard); - } - self.condition_depth -= 1; - self.extract_literals_from_expr(*arm.body); - } - } - // TODO: are closure argument patterns relevant? - Expr::Closure(expr) => self.extract_literals_from_expr(*expr.body), - Expr::Unsafe(expr) => self.extract_literals_from_stmts(expr.block.stmts), - Expr::Block(expr) => self.extract_literals_from_stmts(expr.block.stmts), - Expr::Assign(expr) => { - self.extract_literals_from_expr(*expr.left); - self.extract_literals_from_expr(*expr.right); - } - Expr::AssignOp(expr) => { - self.extract_literals_from_expr(*expr.left); - self.extract_literals_from_expr(*expr.right); - } - Expr::Field(expr) => self.extract_literals_from_expr(*expr.base), - Expr::Index(expr) => { - self.extract_literals_from_expr(*expr.expr); - self.extract_literals_from_expr(*expr.index); - } - // from is enough as we can trigger that range with the beginning - Expr::Range(expr) => { - if let Some(val) = expr.from.or(expr.to) { - self.extract_literals_from_expr(*val); - } - } - Expr::Path(_) => (), - Expr::Reference(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Break(expr) => { - if let Some(expr) = expr.expr { - self.extract_literals_from_expr(*expr); - } - } - Expr::Continue(_) => (), - Expr::Return(expr) => { - if let Some(expr) = expr.expr { - self.extract_literals_from_expr(*expr); - } - } - Expr::Macro(_) => (), - Expr::Struct(expr) => { - for field in expr.fields { - self.extract_literals_from_expr(field.expr); - } - if let Some(expr) = expr.rest { - self.extract_literals_from_expr(*expr); - } - } - Expr::Repeat(expr) => { - self.extract_literals_from_expr(*expr.expr); - self.extract_literals_from_expr(*expr.len); - } - Expr::Paren(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Group(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Try(expr) => self.extract_literals_from_expr(*expr.expr), - Expr::Async(expr) => self.extract_literals_from_stmts(expr.block.stmts), - Expr::TryBlock(expr) => self.extract_literals_from_stmts(expr.block.stmts), - Expr::Yield(expr) => { - if let Some(expr) = expr.expr { - self.extract_literals_from_expr(*expr); - } - } - Expr::Verbatim(_) => unimplemented!("Expr::Verbatim"), - } - } - - fn extract_literals_from_pat(&mut self, pat: Pat) { - match pat { - Pat::Wild(_) | Pat::Path(_) => (), - Pat::Ident(pat) => { - if let Some((_, pat)) = pat.subpat { - self.extract_literals_from_pat(*pat); - } - } - Pat::Struct(pat) => { - for pat in pat.fields { - self.extract_literals_from_pat(*pat.pat); - } - } - Pat::TupleStruct(pat) => { - for pat in pat.pat.front.into_iter().chain(pat.pat.back) { - self.extract_literals_from_pat(pat); - } - } - Pat::Tuple(pat) => { - for pat in pat.front.into_iter().chain(pat.back) { - self.extract_literals_from_pat(pat); - } - } - Pat::Box(pat) => self.extract_literals_from_pat(*pat.pat), - Pat::Ref(pat) => self.extract_literals_from_pat(*pat.pat), - Pat::Lit(pat) => self.extract_literals_from_expr(*pat.expr), - // handling lo is enough as we can trigger that pattern with the lo element already - Pat::Range(pat) => self.extract_literals_from_expr(*pat.lo), - Pat::Slice(pat) => { - let pats_iter = pat - .front - .into_iter() - .chain(pat.middle.map(|pat| *pat)) - .chain(pat.back); - for pat in pats_iter { - self.extract_literals_from_pat(pat); - } - } - Pat::Macro(_) => (), - Pat::Verbatim(_) => unimplemented!("Pat::Verbatim"), - } - } -} diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 5cc94ea9..b34a6907 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -2,6 +2,13 @@ [Guide](index.md) - [Cheat sheet](cheat-sheet.md) +- [Developer guide](dev/index.md) + - [Block Structure Parsing](dev/block-parsing.md) + - [Inline Processing](dev/inline-processing.md) + - [String Handling](dev/string-handling.md) + - [HTML Generation](dev/html-generation.md) + - [Performance Optimizations](dev/performance.md) + - [Adding Extensions](dev/extensions.md) - [Code examples](examples/index.md) - [broken-link-callbacks.rs](examples/broken-link-callbacks.md) - [event-filter.rs](examples/event-filter.md) @@ -10,6 +17,7 @@ - [parser-map-event-print.rs](examples/parser-map-event-print.md) - [parser-map-tag-print.rs](examples/parser-map-tag-print.md) - [string-to-string.rs](examples/string-to-string.md) + - [normalize-wikilink.rs](examples/normalize-wikilink.md) --- - [Detailed Specifications](specs.md) @@ -25,3 +33,4 @@ - [math](./specs/math.md) - [heading attributes](./specs/heading_attrs.md) - [metadata blocks](./specs/metadata_blocks.md) + - [wikilinks](./specs/wikilinks.md) diff --git a/guide/src/cheat-sheet.md b/guide/src/cheat-sheet.md index f22f9833..6fd0c8ec 100644 --- a/guide/src/cheat-sheet.md +++ b/guide/src/cheat-sheet.md @@ -448,4 +448,16 @@ footnote [1]

Custom heading

+ + +[WikiLinks](specs/wikilinks.md) + + + + [[https://example.com/destination|label]] + + + +label + diff --git a/guide/src/dev/block-parsing.md b/guide/src/dev/block-parsing.md new file mode 100644 index 00000000..5a8d5ae1 --- /dev/null +++ b/guide/src/dev/block-parsing.md @@ -0,0 +1,168 @@ +# Block Structure Parsing + +The first pass of pulldown-cmark's parsing process handles block-level elements and constructs the basic document structure. + +It roughly corresponds to Phase 1 of the CommonMark spec appendix ["A Parsing Strategy"](https://spec.commonmark.org/0.31.2/#appendix-a-parsing-strategy). +This chapter explains how the block parsing works in pulldown-cmark. + +## Overview + +Block parsing is implemented in `firstpass.rs` and has two main responsibilities: + +1. Identifying block-level elements like paragraphs, lists, and code blocks +2. Building a tree structure representing the nesting of these blocks + +The block parser operates line-by-line, maintaining a stack of currently open blocks (called the "spine") and handling both container blocks (like blockquotes) and leaf blocks (like paragraphs). + +## Block Types + +The main block types handled by the first pass are: + +- Container blocks: + - Block quotes + - Lists (ordered and unordered) + - List items + - Footnote definitions + +- Leaf blocks: + - Paragraphs + - Headings (ATX and Setext style) + - Code blocks (fenced and indented) + - HTML blocks + - Thematic breaks (horizontal rules) + - Tables (with GFM extension) + +## The Parsing Process + +The block parsing process works like this: + +1. Input text is processed line by line + +2. For each line: + - Check if it continues any blocks from the current spine + - Scan for the start of any new blocks + - Handle transitions between blocks + - Track indentation and container prefixes + +3. Build tree nodes for each block encountered + +4. Handle tight/loose list detection + +Here's a simplified example of how a nested list is parsed: + +```markdown +- First item + - Nested item + with continuation +- Second item +``` + +The parser: +1. Recognizes the first `-` as starting a list and list item +2. Sees the next `-` as starting a nested list +3. Identifies the indented line as continuing the nested item +4. Recognizes the unindented `-` as closing the nested list + +## Tree Construction + +The block structure is stored in a `Tree` where each node contains: + +```rust +struct Item { + start: usize, // Start byte offset + end: usize, // End byte offset + body: ItemBody, // Type and attributes +} + +struct Node { + child: Option, // First child node + next: Option, // Next sibling + item: T, // Node data (T = Item in our tree) +} +``` + +The tree is built incrementally as blocks are parsed. Key operations: + +- `push()`: Move down into a new block's children +- `pop()`: Move back up to the parent block +- `append()`: Add a new sibling block +- `truncate_siblings()`: End open blocks at a certain point + +## Container Block Handling + +Container blocks like blockquotes and lists require special handling: + +1. Track container prefixes (>, -, 1., etc) +2. Calculate correct indentation levels +3. Handle lazy continuation lines +4. Determine tight/loose status for lists + +The `LineStart` struct helps manage this by: +- Tracking indentation and remaining space +- Scanning container markers +- Handling tab stops correctly + +## Leaf Block Processing + +Leaf blocks are handled by specific scanner functions that: + +1. Identify the block type +2. Calculate its bounds +3. Handle internal structure like table columns +4. Manage transitions between blocks + +For example, table parsing: +```rust +pub(crate) fn scan_table_head(data: &[u8]) -> (usize, Vec) { + // Check initial conditions + let (mut i, spaces) = calc_indent(data, 4); + if spaces > 3 || i == data.len() { + return (0, vec![]); + } + + // Parse cells and alignments + let mut cols = vec![]; + let mut active_col = Alignment::None; + // ... + + // Return parsed structure + (i, cols) +} +``` + +## Error Recovery + +The parser is designed to be robust and recover from invalid syntax: + +- Malformed containers fall back to paragraphs +- Invalid indentation is normalized +- Unclosed blocks are implicitly closed +- HTML parsing has fallback modes + +This ensures it can handle real-world Markdown without failing. + +## Interfacing with Inline Parsing + +The block parser prepares for inline parsing by: + +1. Identifying inline-containing blocks +2. Marking potential inline boundaries (e.g. `MaybeLinkOpen`) +3. Providing context (like table cells) +4. Tracking source positions + +The tree structure is then used by the inline parser to process inline elements within the appropriate blocks. + +## Implementation Notes + +Some key implementation details: + +- Line scanning is optimized using SIMD on x86_64 +- The tree structure uses indexed nodes to avoid lifetimes +- Container context is maintained in a stack-like structure +- Source positions are tracked for use by the inline parser and [`OffsetIter`](https://docs.rs/pulldown-cmark/latest/pulldown_cmark/struct.OffsetIter.html) + +The block parser aims to be: +- Fast for common cases +- Memory efficient +- Robust against bad input +- Compliant with CommonMark diff --git a/guide/src/dev/extensions.md b/guide/src/dev/extensions.md new file mode 100644 index 00000000..6557df51 --- /dev/null +++ b/guide/src/dev/extensions.md @@ -0,0 +1,269 @@ +# Adding Extensions + +This guide explains how to add new extensions to pulldown-cmark. Extensions allow you to parse additional Markdown syntax beyond the CommonMark specification. + +If you are looking to get your extension merged upstream, it's a good idea to discuss it with the maintainers before getting to work. + +## Overview + +Adding an extension typically requires: + +1. Adding a feature flag in the `Options` bitflags +2. Adding any new data structures needed to represent the extension's AST nodes +3. Implementing block parsing in `firstpass.rs` if the extension adds block-level elements +4. Implementing inline parsing in `parse.rs` if the extension adds inline elements +5. Adding HTML rendering support in `html.rs` +6. Adding tests to verify the extension works correctly + +Let's walk through each of these steps in detail. + +## Adding the Feature Flag + +Extensions are controlled via the `Options` bitflags defined in `lib.rs`. Add a new constant using the next available bit: + +```rust +bitflags::bitflags! { + pub struct Options: u32 { + // Existing options... + const ENABLE_MY_EXTENSION = 1 << N; // N is next available bit + } +} +``` + +This allows users to enable your extension with: + +```rust +let mut options = Options::empty(); +options.insert(Options::ENABLE_MY_EXTENSION); +``` + +## Adding AST Data Structures + +Extensions often need new AST node types to represent their syntax. These are defined in several places: + +- `Tag` enum in `lib.rs` for container elements +- `TagEnd` enum in `lib.rs` for end tags +- `Event` enum in `lib.rs` for new event types +- `ItemBody` enum in `parse.rs` for internal AST nodes + +For example, the tables extension defines: + +```rust +// In lib.rs +pub enum Tag<'a> { + // ... + Table(Vec), + TableHead, + TableRow, + TableCell, +} + +// In parse.rs +pub(crate) enum ItemBody { + // ... + Table(AlignmentIndex), + TableHead, + TableRow, + TableCell, +} +``` + +Follow existing patterns for naming and make sure to implement all the necessary traits (`Debug`, `Clone`, etc.). + +## Implementing Block Parsing + +If your extension adds block-level elements (like tables, footnotes, etc.), you'll need to: + +1. Add scanning functions in `scanners.rs` to detect your syntax +2. Add parsing logic in `firstpass.rs` to build the block structure +3. Update the `scan_containers()` function if your blocks can be nested + +For example, the tables extension adds: + +```rust +// In scanners.rs +pub(crate) fn scan_table_head(data: &[u8]) -> (usize, Vec) { + // Scan table header row syntax... +} + +// In firstpass.rs +impl<'a> FirstPass<'a, 'b> { + fn parse_table(&mut self, ...) -> Option { + // Parse table structure... + } +} +``` + +Follow these guidelines when implementing block parsing: + +- Use the `scan_` prefix for low-level scanning functions +- Make scanning functions return the number of bytes consumed +- Handle edge cases like empty lines and indentation +- Properly integrate with the container block structure +- Follow the parsing strategies used by existing extensions + +## Implementing Inline Parsing + +If your extension adds inline elements (like strikethrough, math, etc.), you'll need to: + +1. Add marker detection in `parse_line()` in `firstpass.rs` +2. Add opener/closer matching logic in `handle_inline()` +3. Add conversion from internal AST to events + +For example, the strikethrough extension adds: + +```rust +// In firstpass.rs +impl<'a, 'b> FirstPass<'a, 'b> { + fn parse_line(&mut self, ..) -> (usize, Option) { + match byte { + b'~' => { + // Handle tilde markers... + } + } + } +} +``` + +Inline parsing tips: + +- Use the `MaybeX` pattern for markers that need matching +- Handle backslash escaping correctly +- Support nested inline elements +- Follow CommonMark rules for [flanking](https://spec.commonmark.org/0.31.2/#delimiter-run) conditions +- Reuse existing inline parsing infrastructure + +## Adding HTML Rendering + +HTML rendering is handled in `html.rs`. You'll need to: + +1. Add HTML tag generation for your new elements +2. Update the `body_to_tag_end()` and `item_to_event()` functions +3. Handle any special rendering requirements + +For example: + +```rust +// In html.rs +impl<'a, I, W> HtmlWriter<'a, I, W> { + fn start_tag(&mut self, tag: Tag<'a>) -> Result<(), W::Error> { + match tag { + Tag::MyExtension => { + self.write("") + } + // ... + } + } +} +``` + +HTML rendering tips: + +- Follow HTML5 standards +- Handle escaping properly +- Consider accessibility +- Test in different contexts + +## Testing + +Add tests to verify your extension works correctly. pulldown-cmark is principally tested with spec documents, which are Markdown files containing test cases. Each extension should have a file under `specs/` explaining how the feature works along with test cases. Have a look at the existing specs for inspiration. +Other kinds of testing you should consider: + +1. Unit tests alongside implementation +2. Integration tests in `tests/` +3. round-trip tests +4. Edge case tests +5. Interaction tests with other extensions + +For example: + +```rust +#[test] +fn test_my_extension() { + let input = "Test my extension syntax"; + let mut options = Options::empty(); + options.insert(Options::ENABLE_MY_EXTENSION); + let parser = Parser::new_ext(input, options); + // Test parsing result... +} +``` + +Testing tips: + +- Test both positive and negative cases +- Test interactions with other syntax +- Test error conditions +- Test HTML output +- Test with different options enabled +- Run the different fuzzers to find crashes (`fuzz/` parse target) and performance issues (`dos-fuzzer/`) + +## Example: Adding Subscript Extension + +Here's a complete example of adding a hypothetical subscript extension that uses `~text~` for subscript: + +```rust +// In lib.rs +bitflags::bitflags! { + pub struct Options: u32 { + const ENABLE_SUBSCRIPT = 1 << 15; + } +} + +pub enum Tag<'a> { + Subscript, +} + +// In parse.rs +pub(crate) enum ItemBody { + MaybeSubscript(usize), // For opener/closer matching + Subscript, +} + +impl<'a, F> Parser<'a, F> { + fn parse_line(&mut self, ..) -> (usize, Option) { + match byte { + b'~' => { + // Handle subscript markers... + } + } + } +} + +// In html.rs +impl<'a, I, W> HtmlWriter<'a, I, W> { + fn start_tag(&mut self, tag: Tag<'a>) -> Result<(), W::Error> { + match tag { + Tag::Subscript => self.write(""), + } + } +} +``` + +## Tips and Best Practices + +- Study existing extensions for patterns to follow +- Keep parsing efficient +- Handle edge cases gracefully +- Document your extension thoroughly +- Consider adding feature flags for subfeatures +- Follow CommonMark principles where possible +- Test extensively +- Consider compatibility with other extensions + +## Common Pitfalls + +- Not handling nested elements correctly +- Improper escaping in HTML output +- Not following CommonMark precedence rules +- Inefficient parsing of large documents +- Poor error recovery +- Not handling edge cases +- Breaking existing syntax +- Not documenting limitations + +## Further Reading + +- [CommonMark Spec](https://spec.commonmark.org/) +- [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) +- [Existing pulldown-cmark extension specs](https://pulldown-cmark.github.io/pulldown-cmark/specs.html) +- [HTML5 Spec](https://html.spec.whatwg.org/) diff --git a/guide/src/dev/html-generation.md b/guide/src/dev/html-generation.md new file mode 100644 index 00000000..d703980b --- /dev/null +++ b/guide/src/dev/html-generation.md @@ -0,0 +1,177 @@ +# HTML Generation + +This chapter explains how pulldown-cmark generates HTML output from Markdown events. + +## Overview + +HTML generation is implemented in the `html` module and consists of two main components: + +1. The `HtmlWriter` struct which manages state and writes HTML tags +2. Helper functions for converting events to HTML tags and handling special cases + +The HTML generation process works by: +1. Taking an iterator of Markdown events +2. Converting each event into corresponding HTML tags +3. Managing state for special cases like tables and tight lists +4. Writing the HTML tags to the provided output + +## The HtmlWriter + +The core type responsible for HTML generation is `HtmlWriter`: + +```rust +struct HtmlWriter<'a, I, W> { + iter: I, // Iterator supplying events + writer: W, // Writer to write to + end_newline: bool, // Whether last write ended with newline + in_non_writing_block: bool, // In metadata block (no output) + table_state: TableState, // Current state for table processing + table_alignments: Vec, // Column alignments for current table + table_cell_index: usize, // Current cell index in table row + numbers: HashMap, usize>, // For footnote numbering +} +``` + +The writer keeps track of: + +- The current table state (head vs body) +- Table column alignments +- Current cell index +- Footnote numbering +- Whether we're in a non-writing block like metadata +- Whether the last write ended with a newline + +## Event Processing + +The main event processing loop lives in `HtmlWriter::run()`. For each event: + +1. The event is matched and dispatched to the appropriate handler +2. HTML tags are written based on the event type +3. State is updated as needed + +Key event handling patterns: + +### Block Elements + +Block elements like paragraphs, headings, lists etc. are wrapped in HTML tags: + +```rust +match event { + Start(Tag::Paragraph) => write("

"), + End(EndTag::Paragraph) => write("

\n"), + // etc +} +``` + +### Inline Elements + +Inline elements like emphasis and links are handled similarly but without newlines: + +```rust +match event { + Start(Tag::Emphasis) => write(""), + End(EndTag::Emphasis) => write(""), + // etc +} +``` + +### Text Content + +Text content is HTML escaped and written directly: + +```rust +match event { + Text(text) => escape_html_body_text(&mut writer, &text), + // etc +} +``` + +### Complex Elements + +More complex elements like tables require managing state: + +```rust +match event { + Start(Tag::Table(alignments)) => { + self.table_alignments = alignments; + self.write("")?; + } + // etc +} +``` + +## HTML Safety + +The functions `escape_html()` and ``escape_href()`` are used throughout the library for escaping special characters. The escaping functions live in the `pulldown-cmark-escape` crate. + +## Writer Interface + +The HTML writer is generic over the writer type `W`, allowing output to: + +- Strings via `fmt::Write` +- Files/IO via `io::Write` + +This generic design lets users choose the most efficient output method for their use case. For example: +- Using `String` is convenient for in-memory processing and testing +- Using `BufWriter` is efficient for writing directly to disk +- Using a network socket allows streaming HTML over a connection +- Using a custom writer enables special handling like compression or logging + +The `StrWrite` trait provides a common interface to abstract over these different writers: + +```rust +pub trait StrWrite { + type Error; + fn write_str(&mut self, s: &str) -> Result<(), Self::Error>; +} +``` + +This abstraction over the writer type means the HTML generation code can focus on correct tag generation and structure without worrying about the specific output destination. It also allows users to easily integrate pulldown-cmark's HTML output into their existing I/O pipelines. + +## Public API + +The main public API consists of: + +```rust +// Write HTML to a String +pub fn push_html<'a, I>(s: &mut String, iter: I) +where I: Iterator> + +// Write HTML to an IO writer +pub fn write_html_io<'a, I, W>(writer: W, iter: I) -> io::Result<()> +where I: Iterator>, + W: io::Write + +// Write HTML to a fmt writer +pub fn write_html_fmt<'a, I, W>(writer: W, iter: I) -> fmt::Result +where I: Iterator>, + W: fmt::Write +``` + +## Performance Considerations + +HTML generation aims to be efficient by: + +1. Minimizing string allocations +2. Using buffered writers +3. Avoiding recursion in the core loop + +Note: + +```rust +// Using unbuffered writers (like Files) will be slow +// Wrap them in BufWriter for better performance +let file = BufWriter::new(File::create("output.html")?); +write_html_io(file, parser); +``` + +This ensures good performance even with large documents. + +## Customization + +The HTML output can be customized by: + +1. Using a custom writer implementation +2. Preprocessing the event stream +3. Post-processing the HTML output +4. Using the parser options to enable/disable features diff --git a/guide/src/dev/index.md b/guide/src/dev/index.md new file mode 100644 index 00000000..4c853049 --- /dev/null +++ b/guide/src/dev/index.md @@ -0,0 +1,62 @@ +# Developer Guide + +pulldown-cmark uses a two-pass parsing strategy with a pull parser architecture to efficiently parse Markdown into HTML. This guide explains the internal workings of the library for developers who want to contribute or better understand how it works. + +## High-Level Architecture + +The parser operates in two main passes: + +1. **First Pass (Block Structure)**: The first pass scans the document and builds a tree structure representing block-level elements like paragraphs, lists, code blocks, etc. This establishes the hierarchical structure of the document. + +2. **Second Pass (Inline Processing)**: The second pass processes inline elements like emphasis, links and code spans within the blocks identified by the first pass. This is done in a streaming fashion as events are requested. + +The library uses a pull parser design, which means: + +- Instead of pushing events to a callback or building a complete AST, it provides an iterator interface that lets consumers pull events as needed +- It enables flexible transformation of the event stream before rendering + +Key components: + +- `Parser`: The main entry point that implements the Iterator trait for Events +- `Tree`: A Vec-based data structure that holds the block structure +- `Event`: An enum representing the different Markdown elements +- `HtmlWriter`: Renders the event stream as HTML + +## Performance Characteristics + +The parser is designed for high performance: + +- Performance is intended to be linear with respect to the size of the input text +- String handling uses copy-on-write semantics to avoid unnecessary allocations +- SIMD optimizations are available for scanning text on x86_64 + +## Extending the Parser + +The parser can be extended in several ways: + +- New syntax extensions can be added by implementing new scan functions +- The event stream can be transformed using Iterator adaptors +- Custom renderers can be built by consuming events +- The HTML renderer can be customized through options + +## Directory Structure + +``` +src/ + firstpass.rs - First pass block structure parsing + scanners.rs - Low-level text scanning functions + parse.rs - Main parser implementation + html.rs - HTML renderer + tree.rs - Tree data structure + entities.rs - HTML entity handling + strings.rs - String types and utilities +``` + +Subsequent chapters cover each of these components in detail: + +1. [Block Structure Parsing](./dev/block-parsing.md) +2. [Inline Processing](./dev/inline-processing.md) +3. [String Handling](./dev/string-handling.md) +4. [HTML Generation](./dev/html-generation.md) +5. [Performance Optimizations](./dev/performance.md) +6. [Adding Extensions](./dev/extensions.md) diff --git a/guide/src/dev/inline-processing.md b/guide/src/dev/inline-processing.md new file mode 100644 index 00000000..e12f07ff --- /dev/null +++ b/guide/src/dev/inline-processing.md @@ -0,0 +1,149 @@ +# Inline Processing + +The second pass of pulldown-cmark's parsing process handles inline elements like emphasis, links, and code spans. + +## Overview + +Inline processing happens during event iteration rather than as a separate full-document pass. When the parser encounters a block that can contain inlines, it processes the inline elements on demand. + +The main inline elements handled are: + +- Emphasis and strong emphasis (* and _) +- Code spans (`) +- Links and images +- HTML tags and entities +- Autolinks +- Extension elements like strikethrough and math + +## Processing Model + +The inline processor: + +1. Scans text for special characters +2. Identifies potential inline markers +3. Resolves matched pairs (like * for emphasis) +4. Handles nested elements +5. Processes escapes and entities + +## Delimiter Handling + +Emphasis-type elements use a sophisticated delimiter handling system: + +1. Identify delimiter runs (consecutive `*`, `_`, etc) +2. Determine if they can open and/or close +3. Match pairs according to CommonMark rules +4. Handle nested cases correctly + + +The `InlineStack` struct manages this: + +```rust +struct InlineStack { + stack: Vec, + lower_bounds: [usize; 9], +} + +struct InlineEl { + start: TreeIndex, + count: usize, // Number of delimiters + run_length: usize, // Full run length + c: u8, // Delimiter character + both: bool, // Can both open and close +} +``` + +## Link Processing + +Link processing involves: + +1. Finding link text in brackets +2. Handling different link types: + - Inline `[text](url)` + - Reference `[text][ref]` + - Collapsed `[ref][]` + - Shortcut `[ref]` + +3. Resolving references in link definitions +4. Processing link destinations and titles + +The link processor maintains a stack to handle nested links and images: + +```rust +struct LinkStackEl { + node: TreeIndex, + ty: LinkStackTy, +} + +enum LinkStackTy { + Link, + Image, + Disabled, // For nested links +} +``` + +## Code Spans + +Code span processing has special rules: + +1. Match backtick sequences of equal length +2. Handle backslash escapes +3. Strip leading/trailing spaces according to spec +4. Prevent misinterpreting internal backticks + +## HTML Processing + +HTML blocks have already been recognized by the block parser. What remains is inline HTML tags between normal text. Handling this involves: + +1. Identifying HTML constructs: + - Tags + - Comments + - CDATA sections + - Processing instructions + +2. Validating structure +3. Preserving content exactly +4. Handling entities + +The HTML processor uses a state machine to track context: + +```rust +struct HtmlScanGuard { + cdata: usize, + processing: usize, + declaration: usize, + comment: usize, +} +``` + +## String Handling + +Inline processing needs efficient string handling: + +1. Copy-on-write strings to avoid allocation +2. Smart handling of escaped characters +3. Entity resolution +4. UTF-8 awareness + +The `CowStr` type provides this and is documented in detail [here](./string-handling.md). + +## Event Generation + +As inline elements are processed, they generate events: + +1. Start/end events for container elements +2. Text events for content +3. Specialized events for atomic elements +4. Source position tracking + +Events are yielded in document order: + +```rust +enum Event<'a> { + Start(Tag<'a>), + End(TagEnd), + Text(CowStr<'a>), + Code(CowStr<'a>), + Html(CowStr<'a>), + // ... +} +``` diff --git a/guide/src/dev/performance.md b/guide/src/dev/performance.md new file mode 100644 index 00000000..b17950c0 --- /dev/null +++ b/guide/src/dev/performance.md @@ -0,0 +1,114 @@ +# Performance Optimizations + +This chapter covers the key performance optimizations implemented in pulldown-cmark. The library uses several techniques to achieve fast Markdown parsing while maintaining standards compliance and a clean architecture. + +## SIMD-Accelerated Character Scanning + +One of the most performance-critical operations in Markdown parsing is scanning text for special characters that may indicate inline markup. pulldown-cmark uses SIMD (Single Instruction Multiple Data) instructions on x86_64 platforms to accelerate this scanning. + +The SIMD optimization is implemented in `scanners.rs` and operates by: + +1. Creating a lookup table of special characters (like `*`, `_`, etc.) +2. Loading 16 bytes at a time into a SIMD register +3. Performing parallel lookups to identify special characters +4. Generating a bitmask indicating which bytes matched + +```rust +// Example from firstpass.rs showing the core scanning logic +#[target_feature(enable = "ssse3")] +unsafe fn compute_mask(lut: &[u8; 16], bytes: &[u8], ix: usize) -> i32 { + let bitmap = _mm_loadu_si128(lut.as_ptr() as *const __m128i); + let input = _mm_loadu_si128(bytes.as_ptr().add(ix) as *const __m128i); + let bitset = _mm_shuffle_epi8(bitmap, input); + let higher_nibbles = _mm_and_si128(_mm_srli_epi16(input, 4), _mm_set1_epi8(0x0f)); + let bitmask = _mm_shuffle_epi8(bitmask_lookup, higher_nibbles); + let tmp = _mm_and_si128(bitset, bitmask); + let result = _mm_cmpeq_epi8(tmp, bitmask); + _mm_movemask_epi8(result) +} +``` + +This SIMD optimization can provide significant speedups when processing large documents, since character scanning is such a common operation. The code falls back to scalar processing when SIMD is not available. + +## Memory-Efficient String Storage + +The library uses a custom string type `CowStr` that can represent strings. Refer to the [string handling](./string-handling.md) documentation for more details on the performance optimizations inherent to this type. + +## Tree Structure Optimization + +The AST (Abstract Syntax Tree) is stored in a vec-based tree structure that provides: + +1. Fast node creation during parsing +2. Efficient tree traversal +3. Memory locality from vector storage + +```rust +pub(crate) struct Tree { + nodes: Vec>, + spine: Vec, + cur: Option, +} + +pub(crate) struct Node { + pub child: Option, + pub next: Option, + pub item: T, +} +``` + +Key optimizations in the tree structure include: + +- Using indices instead of pointers for node references +- Maintaining a "spine" for fast access to ancestor nodes +- Storing nodes contiguously in a vector for better cache usage +- Using non-zero indices to save space in option types + +## Protection Against Pathological Input + +It is important that the parser performance remain linear with respect to the input length, otherwise the parser would find itself vulnerable to potential DOS attacks. This may not be important for all consumers, but for anyone depending on the library to handle user generated content this is critical. + +Several protections are in place to prevent quadratic time or memory usage on malicious input: + +1. Link nesting depth is limited: +```rust +pub(crate) const LINK_MAX_NESTED_PARENS: usize = 32; +``` + +2. Table column expansion is bounded: +```rust +// Limit to prevent quadratic growth from empty cells +const MAX_AUTOCOMPLETED_CELLS: usize = 1 << 18; +``` + +3. Link reference expansion tracking: +```rust +// Track expansion to prevent quadratic growth from reference definitions +let mut link_ref_expansion_limit: usize = text.len().max(100_000); +``` + + +## Key Performance Considerations + +When using the library, keep in mind: + +1. SIMD optimizations require the `simd` feature and x86_64 platform +2. Large documents benefit most from SIMD scanning +4. The parser is designed for streaming, allowing incremental processing +5. Pathological input protection may limit processing of extremely nested or repetitive content + +## Benchmarking + +The library includes benchmarks to measure performance of key operations: + +- String handling with different storage strategies +- Tree operations +- Full document parsing +- Pathological input cases + +When making changes that could affect performance, run the benchmarks to ensure optimizations are effective: + +```bash +cargo bench +``` + +Note that some optimizations (like SIMD) are platform-specific, so testing on multiple platforms may be necessary. diff --git a/guide/src/dev/string-handling.md b/guide/src/dev/string-handling.md new file mode 100644 index 00000000..1fea7cde --- /dev/null +++ b/guide/src/dev/string-handling.md @@ -0,0 +1,129 @@ +# String Handling + +pulldown-cmark uses a specialized string type system optimized for the specific needs of parsing and representing Markdown content. This chapter explains the key components and design decisions of this system. + +## Overview + +The library uses two main custom string types: + +- `CowStr`: A three-word copy-on-write string type that can be owned, borrowed, or inlined +- `InlineStr`: A small string optimized for very short content that can be stored inline + +These types are designed to balance several requirements: + +- Efficient memory usage for the many small string fragments in Markdown +- Zero-copy operation where possible by borrowing from input +- Good performance for string operations needed during parsing +- Safety and correct handling of Unicode + +## CowStr Type + +The `CowStr` enum is the primary string type used throughout the library. It has three variants: + +```rust +pub enum CowStr<'a> { + Boxed(Box), + Borrowed(&'a str), + Inlined(InlineStr), +} +``` + +Each variant serves a specific purpose: + +- `Borrowed`: References strings from the input text with zero copying +- `Inlined`: Stores very short strings (up to ~22 bytes on 64-bit systems) directly in the enum +- `Boxed`: Owns longer strings that need to be heap allocated + +The key feature is that `CowStr` is exactly three words in size regardless of which variant is used. This fixed size makes it efficient to store and pass around. + +### Example Usage + +```rust +// Borrow from input when possible +let borrowed: CowStr = input[0..15].into(); + +// Single chars are inlined +let inline: CowStr = 'x'.into(); + +// Longer strings are boxed +let boxed: CowStr = "a rather long string...".to_string().into(); +``` + +## InlineStr Type + +`InlineStr` is a small string type that can store short strings inline without heap allocation. It consists of: + +- A fixed-size byte array sized to three machine words minus 2 bytes +- A length field using the remaining byte + +```rust +pub struct InlineStr { + inner: [u8; MAX_INLINE_STR_LEN], + len: u8, +} +``` + +The size is chosen to allow `InlineStr` to be stored directly in `CowStr` without increasing its overall size. On 64-bit systems this allows for strings up to 22 bytes. + +```rust +const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::() - 2; +``` + +Key characteristics: + +- Fixed size with no heap allocation +- UTF-8 encoded +- Length limited by available space +- Copy-able since it's a fixed-size type + +## Converting Between Types + +The library provides conversions between various string types: + +```rust +// From str +let cow: CowStr = "text".into(); + +// From String +let cow: CowStr = string.into(); + +// From char +let cow: CowStr = 'x'.into(); + +// From std::borrow::Cow +let cow: CowStr = std_cow.into(); +``` + +It also provides methods to convert into owned types: + +```rust +let string: String = cow_str.into_string(); +let static_cow: CowStr<'static> = cow_str.into_static(); +``` + +## Performance Considerations + +The string system is designed for the performance characteristics needed by a Markdown parser: + +- Minimal copying of input text +- Efficient handling of many small string fragments +- Fast comparisons for link matching + +## Unicode Handling + +As with Rust built-in strings, these types maintain proper UTF-8 encoding: + +- Input validation occurs when creating `InlineStr` +- String operations preserve valid UTF-8 +- Character boundaries are respected when manipulating strings + +## Implementation Details + +When implementing new features that handle strings, follow these guidelines: + +1. Use `CowStr` as the primary string type +2. Borrow from input when possible using `Borrowed` variant +3. Use `InlineStr` for short string literals +4. Convert to `String` only when necessary for buffering +5. Be aware of UTF-8 encoding requirements +6. Consider memory usage patterns when choosing string operations diff --git a/guide/src/examples/index.md b/guide/src/examples/index.md index ad987a78..6420ab51 100644 --- a/guide/src/examples/index.md +++ b/guide/src/examples/index.md @@ -9,3 +9,4 @@ Example | Description [parser-map-event-print.rs](parser-map-event-print.md) | Dump events to the console in flat form [parser-map-tag-print.rs](parser-map-tag-print.md) | Dump block-level tag events to the console in descriptive form [string-to-string.rs](string-to-string.md) | Convert markdown to HTML +[normalize-wikilink.rs](normalize-wikilink.md) | Normalizes wikilinks' destination url diff --git a/guide/src/examples/normalize-wikilink.md b/guide/src/examples/normalize-wikilink.md new file mode 100644 index 00000000..279e2b4e --- /dev/null +++ b/guide/src/examples/normalize-wikilink.md @@ -0,0 +1,9 @@ +Normalizes wikilinks as they pass through the parser. + +The resulting destination url of the wikilink is normalized closely to how +[MediaWiki](https://www.mediawiki.org/wiki/Help:Links) links are normalized. +Use this example to customize this behavior for your use cases. + +```rust +{{#include ../../../pulldown-cmark/examples/normalize-wikilink.rs}} +``` diff --git a/pulldown-cmark-escape/Cargo.toml b/pulldown-cmark-escape/Cargo.toml index 4aa1c04d..7556ce06 100644 --- a/pulldown-cmark-escape/Cargo.toml +++ b/pulldown-cmark-escape/Cargo.toml @@ -16,3 +16,4 @@ readme = "./README.md" [features] simd = [] +std = [] \ No newline at end of file diff --git a/pulldown-cmark-escape/README.md b/pulldown-cmark-escape/README.md index a0c397f3..4345fac0 100644 --- a/pulldown-cmark-escape/README.md +++ b/pulldown-cmark-escape/README.md @@ -1,10 +1,10 @@ # pulldown-cmark-escape [![Tests](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml/badge.svg)](https://github.com/pulldown-cmark/pulldown-cmark/actions/workflows/rust.yml) -[![Docs](https://docs.rs/pulldown-cmark/badge.svg)](https://docs.rs/pulldown-cmark) +[![Docs](https://docs.rs/pulldown-cmark-escape/badge.svg)](https://docs.rs/pulldown-cmark-escape) [![Crates.io](https://img.shields.io/crates/v/pulldown-cmark-escape.svg?maxAge=2592000)](https://crates.io/crates/pulldown-cmark-escape) -[Documentation](https://docs.rs/pulldown-cmark/) +[Documentation](https://docs.rs/pulldown-cmark-escape/) This crate allows to escape HTML and links and it is part of the pulldown-cmark project, by providing `escape_html`, `escape_html_body_text` (for diff --git a/pulldown-cmark-escape/src/lib.rs b/pulldown-cmark-escape/src/lib.rs index 7ee03ae2..cb764023 100644 --- a/pulldown-cmark-escape/src/lib.rs +++ b/pulldown-cmark-escape/src/lib.rs @@ -20,10 +20,23 @@ //! Utility functions for HTML escaping. Only useful when building your own //! HTML renderer. - -use std::fmt::{self, Arguments}; +#![warn( + clippy::alloc_instead_of_core, + clippy::std_instead_of_alloc, + clippy::std_instead_of_core +)] +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +use alloc::string::String; + +use core::fmt::{self, Arguments}; +use core::str::from_utf8; +#[cfg(feature = "std")] use std::io::{self, Write}; -use std::str::from_utf8; #[rustfmt::skip] static HREF_SAFE: [u8; 128] = [ @@ -46,6 +59,7 @@ static SINGLE_QUOTE_ESCAPE: &str = "'"; /// `W: StrWrite`. Since we need the latter a lot, we choose to wrap /// `Write` types. #[derive(Debug)] +#[cfg(feature = "std")] pub struct IoWriter(pub W); /// Trait that allows writing string slices. This is basically an extension @@ -57,6 +71,7 @@ pub trait StrWrite { fn write_fmt(&mut self, args: Arguments) -> Result<(), Self::Error>; } +#[cfg(feature = "std")] impl StrWrite for IoWriter where W: Write, @@ -445,6 +460,8 @@ mod simd { #[cfg(test)] mod test { + use alloc::string::String; + pub use super::{escape_href, escape_html, escape_html_body_text}; #[test] diff --git a/pulldown-cmark/Cargo.toml b/pulldown-cmark/Cargo.toml index c6cc6af8..e04f02e1 100644 --- a/pulldown-cmark/Cargo.toml +++ b/pulldown-cmark/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pulldown-cmark" -version = "0.11.0" +version = "0.13.1" authors = [ "Raph Levien ", "Marcus Klaas de Vries ", @@ -24,7 +24,7 @@ build = "build.rs" [[bin]] name = "pulldown-cmark" -required-features = ["getopts"] +required-features = ["getopts", "std"] doc = false [[example]] @@ -57,25 +57,32 @@ name = "broken-link-callbacks" required-features = ["html"] doc-scrape-examples = true +[[example]] +name = "normalize-wikilink" +required-features = ["html"] +doc-scrape-examples = true + [dependencies] bitflags = "2" unicase = "2.6" -memchr = "2.5" +memchr = { version = "2.5", default-features = false } getopts = { version = "0.2", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } pulldown-cmark-escape = { path = "../pulldown-cmark-escape", version = "0.11", optional = true } +hashbrown = { version = "0.15.2", optional = true } [dev-dependencies] -lazy_static = "1.4" regex = "1.6" serde_json = "1.0.61" bincode = "1.3.1" [features] -default = ["getopts", "html"] +default = ["std", "getopts", "html"] +std = ["pulldown-cmark-escape?/std", "memchr/std"] gen-tests = [] simd = ["pulldown-cmark-escape?/simd"] html = ["pulldown-cmark-escape"] +hashbrown = ["dep:hashbrown"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(rustbuild)'] } diff --git a/pulldown-cmark/build.rs b/pulldown-cmark/build.rs index a7e6462c..8cd7efa6 100644 --- a/pulldown-cmark/build.rs +++ b/pulldown-cmark/build.rs @@ -4,7 +4,7 @@ fn main() { // If the "gen-tests" feature is absent, // this function will be compiled down to nothing -#[cfg(not(feature = "gen-tests"))] +#[cfg(any(not(feature = "gen-tests"), not(feature = "std")))] fn generate_tests_from_spec() {} // If the feature is present, generate tests @@ -18,7 +18,7 @@ fn generate_tests_from_spec() {} // . // expected html output // ```````````````````````````````` -#[cfg(feature = "gen-tests")] +#[cfg(all(feature = "gen-tests", feature = "std"))] fn generate_tests_from_spec() { use std::fs::{self, File}; use std::io::{Read, Write}; @@ -86,7 +86,7 @@ fn {}_test_{i}() {{ let original = r##"{original}"##; let expected = r##"{expected}"##; - test_markdown_html(original, expected, {smart_punct}, {metadata_blocks}, {old_footnotes}); + test_markdown_html(original, expected, {smart_punct}, {metadata_blocks}, {old_footnotes}, {subscript}, {wikilinks}, {deflists}, {container_extensions}); }} "###, spec_name, @@ -96,6 +96,10 @@ fn {}_test_{i}() {{ smart_punct = testcase.smart_punct, metadata_blocks = testcase.metadata_blocks, old_footnotes = testcase.old_footnotes, + subscript = testcase.subscript, + wikilinks = testcase.wikilinks, + deflists = testcase.deflists, + container_extensions = testcase.container_extensions, )) .unwrap(); @@ -151,6 +155,10 @@ pub struct TestCase { pub smart_punct: bool, pub metadata_blocks: bool, pub old_footnotes: bool, + pub subscript: bool, + pub wikilinks: bool, + pub deflists: bool, + pub container_extensions: bool, } #[cfg(feature = "gen-tests")] @@ -161,38 +169,115 @@ impl<'a> Iterator for Spec<'a> { let spec = self.spec; let prefix = "```````````````````````````````` example"; - let (i_start, smart_punct, metadata_blocks, old_footnotes) = - self.spec.find(prefix).and_then(|pos| { - let smartpunct_suffix = "_smartpunct\n"; - let metadata_blocks_suffix = "_metadata_blocks\n"; - let old_footnotes_suffix = "_old_footnotes\n"; - if spec[(pos + prefix.len())..].starts_with(smartpunct_suffix) { - Some(( - pos + prefix.len() + smartpunct_suffix.len(), - true, - false, - false, - )) - } else if spec[(pos + prefix.len())..].starts_with(metadata_blocks_suffix) { - Some(( - pos + prefix.len() + metadata_blocks_suffix.len(), - false, - true, - false, - )) - } else if spec[(pos + prefix.len())..].starts_with(old_footnotes_suffix) { - Some(( - pos + prefix.len() + old_footnotes_suffix.len(), - false, - false, - true, - )) - } else if spec[(pos + prefix.len())..].starts_with('\n') { - Some((pos + prefix.len() + 1, false, false, false)) - } else { - None - } - })?; + let ( + i_start, + smart_punct, + metadata_blocks, + old_footnotes, + subscript, + wikilinks, + deflists, + container_extensions, + ) = self.spec.find(prefix).and_then(|pos| { + let smartpunct_suffix = "_smartpunct\n"; + let metadata_blocks_suffix = "_metadata_blocks\n"; + let old_footnotes_suffix = "_old_footnotes\n"; + let super_sub_suffix = "_super_sub\n"; + let wikilinks_suffix = "_wikilinks\n"; + let deflists_suffix = "_deflists\n"; + let container_extensions_suffix = "_container_extensions\n"; + if spec[(pos + prefix.len())..].starts_with(smartpunct_suffix) { + Some(( + pos + prefix.len() + smartpunct_suffix.len(), + true, + false, + false, + false, + false, + false, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(metadata_blocks_suffix) { + Some(( + pos + prefix.len() + metadata_blocks_suffix.len(), + false, + true, + false, + false, + false, + false, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(old_footnotes_suffix) { + Some(( + pos + prefix.len() + old_footnotes_suffix.len(), + false, + false, + true, + false, + false, + false, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(super_sub_suffix) { + Some(( + pos + prefix.len() + super_sub_suffix.len(), + false, + false, + false, + true, + false, + false, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(wikilinks_suffix) { + Some(( + pos + prefix.len() + wikilinks_suffix.len(), + false, + false, + false, + false, + true, + false, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(deflists_suffix) { + Some(( + pos + prefix.len() + deflists_suffix.len(), + false, + false, + false, + false, + false, + true, + false, + )) + } else if spec[(pos + prefix.len())..].starts_with(container_extensions_suffix) { + Some(( + pos + prefix.len() + container_extensions_suffix.len(), + false, + false, + false, + false, + false, + false, + true, + )) + } else if spec[(pos + prefix.len())..].starts_with('\n') { + Some(( + pos + prefix.len() + 1, + false, + false, + false, + false, + false, + false, + false, + )) + } else { + None + } + })?; let i_end = self.spec[i_start..] .find("\n.\n") @@ -210,6 +295,10 @@ impl<'a> Iterator for Spec<'a> { smart_punct, metadata_blocks, old_footnotes, + subscript, + wikilinks, + deflists, + container_extensions, }; Some(test_case) diff --git a/pulldown-cmark/examples/footnote-rewrite.rs b/pulldown-cmark/examples/footnote-rewrite.rs index a550c3e3..00b84fb9 100644 --- a/pulldown-cmark/examples/footnote-rewrite.rs +++ b/pulldown-cmark/examples/footnote-rewrite.rs @@ -1,6 +1,4 @@ -use std::collections::HashMap; -use std::fmt::Write as _; -use std::io::Write as _; +use std::{collections::HashMap, fmt::Write as _, io::Write as _}; use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag, TagEnd}; diff --git a/pulldown-cmark/examples/normalize-wikilink.rs b/pulldown-cmark/examples/normalize-wikilink.rs new file mode 100644 index 00000000..e2d505a2 --- /dev/null +++ b/pulldown-cmark/examples/normalize-wikilink.rs @@ -0,0 +1,93 @@ +use std::io::Write; + +use pulldown_cmark::{html, CowStr, Event, LinkType, Options, Parser, Tag}; +use regex::RegexBuilder; + +/// This example demonstrates how to normalize the href of a wikilink. The +/// details of this implementation can be tweaked for different use cases. +fn main() { + let markdown_input: &str = r#" +Example provided by [[https://example.org/]]. +Some people might prefer the wikilink syntax for autolinks. + +Wanna go for a [[Wiki Walk]]?"#; + + let parser = Parser::new_ext(markdown_input, Options::ENABLE_WIKILINKS).map(|event| { + if let Event::Start(Tag::Link { + link_type: LinkType::WikiLink { has_pothole }, + dest_url, + title, + id, + }) = event + { + let new_link = normalize_wikilink(dest_url); + Event::Start(Tag::Link { + link_type: LinkType::WikiLink { has_pothole }, + dest_url: new_link, + title, + id, + }) + } else { + event + } + }); + + // Write to anything implementing the `Write` trait. This could also be a file + // or network socket. + let stdout = std::io::stdout(); + let mut handle = stdout.lock(); + handle.write_all(b"\nHTML output:\n").unwrap(); + html::write_html_io(&mut handle, parser).unwrap(); +} + +/// Performs wikilink normalization. +fn normalize_wikilink(link: CowStr) -> CowStr { + // your wiki is stored at "/wiki" + let prefix: &str = "/wiki"; + if link.is_empty() { + return link; + } + + // check if the link is absolute, if it is, return as is + // according to RFC 3986; https://www.rfc-editor.org/rfc/rfc3986 + let is_absolute = RegexBuilder::new("^(?:[a-z][a-z0-9+\\-.]*:)?//") + .case_insensitive(true) + .build() + .expect("valid regex"); + + if is_absolute.is_match(&link) { + return link; + } + + let mut result = String::with_capacity(link.len() + 2); + let mut i = 0; + let mut mark = 0; + let mut in_whitespace = false; + + result.push_str(prefix); + + if !link.starts_with('/') { + result.push('/'); + } + + while i < link.len() { + if !in_whitespace && link.as_bytes()[i].is_ascii_whitespace() { + in_whitespace = true; + result.push_str(&link[mark..i]); + } else if in_whitespace && !link.as_bytes()[i].is_ascii_whitespace() { + result.push('_'); + mark = i; + in_whitespace = false; + } + + i += 1; + } + + if !in_whitespace { + result.push_str(&link[mark..]); + } + if !result.ends_with('/') { + result.push('/'); + } + result.into() +} diff --git a/pulldown-cmark/examples/parser-map-tag-print.rs b/pulldown-cmark/examples/parser-map-tag-print.rs index 30b1919e..cb83c691 100644 --- a/pulldown-cmark/examples/parser-map-tag-print.rs +++ b/pulldown-cmark/examples/parser-map-tag-print.rs @@ -60,14 +60,23 @@ fn main() { "List ordered_list_first_item_number: {:?}", ordered_list_first_item_number ), + Tag::DefinitionList => println!("Definition list"), + Tag::DefinitionListTitle => println!("Definition title (definition list item)"), + Tag::DefinitionListDefinition => println!("Definition (definition list item)"), Tag::Item => println!("Item (this is a list item)"), Tag::Emphasis => println!("Emphasis (this is a span tag)"), + Tag::Superscript => println!("Superscript (this is a span tag)"), + Tag::Subscript => println!("Subscript (this is a span tag)"), Tag::Strong => println!("Strong (this is a span tag)"), Tag::Strikethrough => println!("Strikethrough (this is a span tag)"), + Tag::Highlight => println!("Highlight (this is a span tag)"), Tag::BlockQuote(kind) => println!("BlockQuote ({:?})", kind), Tag::CodeBlock(code_block_kind) => { println!("CodeBlock code_block_kind: {:?}", code_block_kind) } + Tag::ContainerBlock(kind, summary) => { + println!("ContainerBlock ({:?}) summary: {:?}", kind, summary) + } Tag::Link { link_type, dest_url, diff --git a/pulldown-cmark/specs/container_extensions.txt b/pulldown-cmark/specs/container_extensions.txt new file mode 100644 index 00000000..078b985b --- /dev/null +++ b/pulldown-cmark/specs/container_extensions.txt @@ -0,0 +1,351 @@ +# Spoiler tests + +```````````````````````````````` example_container_extensions +> Is this **bold**? +> Is this **bold**? +> ::: spoiler Is this expandable? +> Is this inside? +> ::: +. +

Is this bold? +Is this bold?

+
+Is this expandable? +

Is this inside?

+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: spoiler Is this expandable? +Is this collapsable? +> Is this **bold**? +> Is this **bold**? +::: +**is this seperate and bold** +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold? +Is this bold?

+
+

is this seperate and bold

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: spoiler Is this expandable? +Is this collapsable? +> Is this **bold**? +> Is this **bold**? +> ::: spoiler Is this expandable? +> Is this inside? +> ::: +::: +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold? +Is this bold?

+
+Is this expandable? +

Is this inside?

+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: spoiler Is this expandable? +Is this collapsable? +> Is this **bold**? +> Is this **bold**? +> ::: spoiler Is this expandable? +> Is this collapsable? +> > Is this **bold**? +> > Is this **bold**? +> ::: +::: +**is this seperate and bold** +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold? +Is this bold?

+
+Is this expandable? +

Is this collapsable?

+

Is this bold? +Is this bold?

+
+
+
+

is this seperate and bold

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: spoiler Is this expandable? +Is this collapsable? +::: +**is this seperate and bold** +. +
+Is this expandable? +

Is this collapsable?

+
+

is this seperate and bold

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: spoiler Is this expandable? +Is this collapsable? + +Is this **bold**? +::: +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold?

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +:::spoiler Is this expandable? +Is this collapsable? + +Is this **bold**? +::: +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold?

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: example +Is this collapsable? + +Is this **bold**? +::: +. +
+

Is this collapsable?

+

Is this bold?

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +:::: example +Is this collapsable? + +Is this **bold**? +:::: +. +
+

Is this collapsable?

+

Is this bold?

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +:::::spoiler Is this expandable? +Is this collapsable? + +Is this **bold**? +::::: +. +
+Is this expandable? +

Is this collapsable?

+

Is this bold?

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: + +content + +::: +. +

:::

+

content

+

:::

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: block + +content + +::: end +. +
+

content

+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions + ::: block +::: + +::: block + ::: +. +
+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: a +::: b + +::: +::: +. +
+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +:::: a +::: b + +::: +:::: +. +
+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: a +:::: b + +:::: +::: +. +
+
+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: +Hi +::: +. +

::: +Hi +:::

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::::: foo +Hi +::: +:::::: +. +
+

Hi +:::

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +> ::: foo +> Hi +. +
+
+

Hi

+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: c_d +Hi +::: +. +
+

Hi

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: container +> shouldn't close, right? +> ::: +> x +::: +. +
+
+

shouldn't close, right? +::: +x

+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: container +> shouldn't close, right? +> ::: +> x + +::: +. +
+
+

shouldn't close, right? +::: +x

+
+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: a +:::: b +::::: c +:::: +x +. +
+
+
+
+
+

x

+
+```````````````````````````````` + +```````````````````````````````` example_container_extensions +::: a +content ::: +. +

content :::

+```````````````````````````````` + +```````````````````````````````` example_container_extensions +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: a +content ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +. +

content :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

+```````````````````````````````` diff --git a/pulldown-cmark/specs/definition_lists.txt b/pulldown-cmark/specs/definition_lists.txt new file mode 100644 index 00000000..c9a079f5 --- /dev/null +++ b/pulldown-cmark/specs/definition_lists.txt @@ -0,0 +1,687 @@ +## Definition lists + +Based on https://github.com/jgm/commonmark-hs/blob/e3747fd282c806cd2f8e83226e85f1b83ee889c3/commonmark-extensions/test/definition_lists.md + +The term is given on a line by itself, followed by +one or more definitions. Each definition must begin +with `:` (after 0-2 spaces); subsequent lines must +be indented unless they are lazy paragraph +continuations. + +Commonmark-HS allows definition lists to use `~`. +We can't do that, because we already use `~` for strikethrough. + +The list is tight if there is no blank line between +the term and the first definition, otherwise loose. + +```````````````````````````````` example_deflists +apple +: red fruit + +orange +: orange fruit +. +
+
apple
+
red fruit
+
orange
+
orange fruit
+
+```````````````````````````````` + +Loose: + +```````````````````````````````` example_deflists +apple + +: red fruit + +orange + +: orange fruit +. +
+
apple
+
+

red fruit

+
+
orange
+
+

orange fruit

+
+
+```````````````````````````````` + +Also loose: + +```````````````````````````````` example_deflists +apple + +: red fruit +. +
+
apple
+
+

red fruit

+
+
+```````````````````````````````` + +Indented marker: + +```````````````````````````````` example_deflists +apple + : red fruit + +orange + : orange fruit +. +
+
apple
+
red fruit
+
orange
+
orange fruit
+
+```````````````````````````````` + +```````````````````````````````` example_deflists +apple + + : red fruit + +orange + + : orange fruit +. +
+
apple
+
+

red fruit

+
+
orange
+
+

orange fruit

+
+
+```````````````````````````````` + +Multiple blocks in a definition: + +```````````````````````````````` example_deflists +*apple* + +: red fruit + + contains seeds, + crisp, pleasant to taste + +*orange* + +: orange fruit + + { orange code block } + + > orange block quote +. +
+
apple
+
+

red fruit

+

contains seeds, +crisp, pleasant to taste

+
+
orange
+
+

orange fruit

+
{ orange code block }
+
+
+

orange block quote

+
+
+
+```````````````````````````````` + +Nested lists: + +```````````````````````````````` example_deflists +term + +: 1. Para one + + Para two +. +
+
term
+
+
    +
  1. Para one

    +

    Para two

  2. +
+
+
+```````````````````````````````` + +Multiple definitions, tight: + +```````````````````````````````` example_deflists +apple +: red fruit +: computer company + +orange +: orange fruit +: telecom company +. +
+
apple
+
red fruit
+
computer company
+
orange
+
orange fruit
+
telecom company
+
+```````````````````````````````` + +Multiple definitions, loose: + +```````````````````````````````` example_deflists +apple + +: red fruit + +: computer company + +orange + +: orange fruit +: telecom company +. +
+
apple
+
+

red fruit

+
+
+

computer company

+
+
orange
+
+

orange fruit

+
+
+

telecom company

+
+
+```````````````````````````````` + +Lazy line continuations: + +```````````````````````````````` example_deflists +apple + +: red fruit + +: computer +company + +orange + +: orange +fruit +: telecom company +. +
+
apple
+
+

red fruit

+
+
+

computer +company

+
+
orange
+
+

orange +fruit

+
+
+

telecom company

+
+
+```````````````````````````````` + +A lazy continuation may start with a `:`, if it has enough indent. + +```````````````````````````````` example_deflists +apple + : > computer company + : red fruit + +orange + : > telecom company + : orange fruit + +chili's + : > restaurant company + : spicy fruit +. +
+
apple
+
+

computer company +: red fruit

+
+
+
orange
+
+

telecom company

+
+
+
orange fruit
+
chili's
+
+

restaurant company

+
+
+
spicy fruit
+
+```````````````````````````````` + +It may not, however, lazily act as a definition in a list. + +```````````````````````````````` example_deflists +> cherry +> : keyboard company +> pomegranate +: tart fruit +. +
+
+
cherry
+
keyboard company +pomegranate +: tart fruit
+
+
+```````````````````````````````` + +Definition terms may span multiple lines: + +```````````````````````````````` example_deflists +a +b\ +c + +: foo +. +
+
a +b
+c
+
+

foo

+
+
+```````````````````````````````` + +Definition list with preceding paragraph +(): + +```````````````````````````````` example_deflists +Foo + +bar +: baz + +bim +: bor +. +

Foo

+
+
bar
+
baz
+
bim
+
bor
+
+```````````````````````````````` + +Definition list followed by paragraph. + +```````````````````````````````` example_deflists +bar +: baz + +bim +: bor + +Bloze +. +
+
bar
+
baz
+
bim
+
bor
+
+

Bloze

+```````````````````````````````` + +The total indentation for a definition list cannot exceed four. +This means you can't have one start with four spaces. + +```````````````````````````````` example_deflists +bar + :baz + +Bloze +. +

bar +:baz

+

Bloze

+```````````````````````````````` + +You can follow one with four spaces, +because it'll "eat" the three spaces after it. + +```````````````````````````````` example_deflists +bar +: baz + +Bloze +. +
+
bar
+
baz
+
+

Bloze

+```````````````````````````````` + +To use an indented code block inside of a definition, +you need to have five spaces of indentation after the colon. + +```````````````````````````````` example_deflists +bar +: baz + +bar + : baz + +bar + : baz + +bar + : baz + +bar + : baz +. +
+
bar
+
+
  baz
+
+
+
bar
+
+
 baz
+
+
+
bar
+
+
baz
+
+
+
bar
+
baz
+
+

bar +: baz

+```````````````````````````````` + + +Because of the way it eats indentation after the colon, the number of +spaces you need for indented code blocks on subsequent lines depends on +the indentation of the earlier block. + +```````````````````````````````` example_deflists +*orange* + +: orange fruit + + { orange code block } + + > orange block quote + +*orange* + +: orange fruit + + { orange code block } + + > orange block quote +. +
+
orange
+
+

orange fruit

+
{ orange code block }
+
+
+
+
+

orange block quote

+
+
+
orange
+
+
orange fruit
+
+  { orange code block }
+
+
+

orange block quote

+
+
+
+```````````````````````````````` + +Definition titles can't be tables. + +```````````````````````````````` example_deflists +Test|Table +----|----- +: first +. +
+ + +
TestTable
: first
+```````````````````````````````` + +```````````````````````````````` example_deflists +first +: second + +Test|Table +----|----- +: fourth +. +
+
first
+
second
+
+ + + +
TestTable
: fourth
+```````````````````````````````` + + +Definition titles can't be headers + +```````````````````````````````` example_deflists +My section +========== +: first +. +

My section

+

: first

+```````````````````````````````` + +```````````````````````````````` example_deflists +first +: second + +My section +========== +: fourth +. +
+
first
+
second
+
+

My section

+

: fourth

+```````````````````````````````` + +```````````````````````````````` example_deflists +## My subsection +: first +. +

My subsection

+

: first

+```````````````````````````````` + +```````````````````````````````` example_deflists +first +: second + +## My subsection +: fourth +. +
+
first
+
second
+
+

My subsection

+

: fourth

+```````````````````````````````` + + +Definition list titles can't be block quotes + +```````````````````````````````` example_deflists +> first +: second +. +
+

first +: second

+
+```````````````````````````````` + + +Definition titles can't end with hard line breaks. + +```````````````````````````````` example_deflists +first\ +: second + +third +: fourth +. +
+
first\
+
second
+
third
+
fourth
+
+```````````````````````````````` + + +Definition titles can't be HTML blocks, but inline's fine. + +```````````````````````````````` example_deflists +
first
+: second + +first +: second +
third
+: fourth +. +
first
+: second +
+
first
+
second
+
+
third
+: fourth +```````````````````````````````` + +```````````````````````````````` example_deflists +first +: second + +third +: fourth + +fifth +: sixth +. +
+
first
+
second
+
third
+
fourth
+
fifth
+
sixth
+
+```````````````````````````````` + +Nested definition lists: +(): + +```````````````````````````````` example_deflists +level one +: l1 + level two + : l2 + level three + : l3 + +level one +: l1 +. +
+
level one
+
+
+
l1 +level two
+
+
+
l2 +level three
+
l3
+
+
+
+
+
level one
+
l1
+
+```````````````````````````````` + +https://github.com/pulldown-cmark/pulldown-cmark/issues/997 + +Definition lists combined with link refdef + +```````````````````````````````` example_deflists +[a]: /url + +: +. +

:

+```````````````````````````````` diff --git a/pulldown-cmark/specs/highlight.txt b/pulldown-cmark/specs/highlight.txt new file mode 100644 index 00000000..1712243d --- /dev/null +++ b/pulldown-cmark/specs/highlight.txt @@ -0,0 +1,166 @@ +Highlighted text uses `==` delimiters, mirroring the markdown-it / pandoc +extension. The pairing rules mirror GFM's `~~` strikethrough. + +# Basic + +```````````````````````````````` example +==hi== +. +

hi

+```````````````````````````````` + +```````````````````````````````` example +==hello world== +. +

hello world

+```````````````````````````````` + +# Nesting with emphasis + +```````````````````````````````` example +==*hi*== +. +

hi

+```````````````````````````````` + +```````````````````````````````` example +*==hi==* +. +

hi

+```````````````````````````````` + +```````````````````````````````` example +==**bold** and *em*== +. +

bold and em

+```````````````````````````````` + +# Single `=` is literal + +```````````````````````````````` example +=hi= +. +

=hi=

+```````````````````````````````` + +```````````````````````````````` example +a = b +. +

a = b

+```````````````````````````````` + +# Whitespace adjacent to inner edges fails flanking + +```````````````````````````````` example +== hi == +. +

== hi ==

+```````````````````````````````` + +```````````````````````````````` example +==hi == +. +

==hi ==

+```````````````````````````````` + +```````````````````````````````` example +== hi== +. +

== hi==

+```````````````````````````````` + +# Triple and quadruple runs do not pair + +```````````````````````````````` example +===hi=== +. +

===hi===

+```````````````````````````````` + +```````````````````````````````` example +====hi==== +. +

====hi====

+```````````````````````````````` + +```````````````````````````````` example +==== +. +

====

+```````````````````````````````` + +# Intraword highlight (mirrors `~~`) + +```````````````````````````````` example +a==b==c +. +

abc

+```````````````````````````````` + +```````````````````````````````` example +==This==is==highlighted== +. +

Thisishighlighted

+```````````````````````````````` + +# Across a soft line break + +```````````````````````````````` example +==a +b== +. +

a +b

+```````````````````````````````` + +# Backslash escapes + +```````````````````````````````` example +==hi \== there== +. +

hi == there

+```````````````````````````````` + +```````````````````````````````` example +\==not highlighted== +. +

==not highlighted==

+```````````````````````````````` + +# Inside other block contexts + +```````````````````````````````` example +- ==in a list== +. +
    +
  • in a list
  • +
+```````````````````````````````` + +```````````````````````````````` example +> ==in a quote== +. +
+

in a quote

+
+```````````````````````````````` + +```````````````````````````````` example +# ==in a heading== +. +

in a heading

+```````````````````````````````` + +# Combined with strikethrough + +```````````````````````````````` example +==~~both~~== +. +

both

+```````````````````````````````` + +```````````````````````````````` example +~~==both==~~ +. +

both

+```````````````````````````````` diff --git a/pulldown-cmark/specs/math.txt b/pulldown-cmark/specs/math.txt index b16453b8..b908127c 100644 --- a/pulldown-cmark/specs/math.txt +++ b/pulldown-cmark/specs/math.txt @@ -399,6 +399,31 @@ $}$] $$

$}$] $$

```````````````````````````````` +The inline math closer cannot be immediately followed by a digit. +The opener can be, though. +Display math isn't subject to this rule. + +```````````````````````````````` example +$1$2$3 + +$1$2$3$ + +$1{$2$}3$ + +$$1$$2$$3 + +$$1$$2$$3$$ + +$$1{$$2$$}3$$ +. +

$1$2$3

+

$1$23

+

1{$2$}3

+

12$$3

+

123

+

1{$$2$$}3

+```````````````````````````````` + ## Edge case tests comparison with GitHub Test cases diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt index 0a2bf3df..12b031fd 100644 --- a/pulldown-cmark/specs/regression.txt +++ b/pulldown-cmark/specs/regression.txt @@ -2635,3 +2635,247 @@ bar

bar

```````````````````````````````` + +ISSUE #655 + +```````````````````````````````` example +` +` +. +

+```````````````````````````````` + +ISSUE #949 + +```````````````````````````````` example_deflists +* def this + : def text def text +. +
    +
  • +
    +
    def this
    +
    def text def text
    +
    +
  • +
+```````````````````````````````` + +```````````````````````````````` example_deflists +* def this + + : def text def text +. +
    +
  • +
    +
    def this
    +

    def text def text

    +
    +
  • +
+```````````````````````````````` + +```````````````````````````````` example_deflists +**A:** + +> B C +> I J :x: K +> :x: L M +> N O _P_ Q R. (S +> T U, V W +> :x:,:x:,:x:, and :x: but no :x: or +> :x:.) +. +

A:

+
B C +I J :x: K
x: L M +N O P Q R. (S +T U, V W
+
x:,:x:,:x:, and :x: but no :x: or
+
x:.)
+```````````````````````````````` + +```````````````````````````````` example_deflists +[abc] check `foobar_raz` + Some preamble `foobar_raz`, not `barfoo_raz` + :D + + This should fix: + + > Something is wrong! +. +
+
[abc] check foobar_raz +Some preamble foobar_raz, not barfoo_raz
+
D
+
+

This should fix:

+

> Something is wrong!

+```````````````````````````````` + +```````````````````````````````` example +- Item definition [it + ```rust + ``` + stuff](https://example.com) +. +
    +
  • Item definition [it +
    +stuff](https://example.com)
  • +
+```````````````````````````````` + +ISSUE #963 + +```````````````````````````````` example +foo +{.class} +=== + +> foo +> {.class} +> === +> +> > foo +> > {.class} +> > === + +* > foo + > {.class} + > === + +> foo +>→{.class} +>→=== +. +

foo +

+
+

foo +

+
+

foo +

+
+
+
    +
  • +
    +

    foo +

    +
    +
  • +
+
+

foo +

+
+```````````````````````````````` + +```````````````````````````````` example +the trailing space after the > should be stripped + > {.bar} +=== +. +

the trailing space after the > should be stripped +>

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +[[Wiki<|Link]] +. +

Link

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +* +. +
    +
  • <c +
  • +
+```````````````````````````````` + +https://github.com/pulldown-cmark/pulldown-cmark/issues/999 + +```````````````````````````````` example_wikilinks +- [x] * some text +- [ ] > some text +- [x] + * some text +- [ ] + > some text +. +
    +
  • +* some text
  • +
  • +> some text
  • +
  • +
      +
    • some text
    • +
  • +
  • +
    +

    some text

    +
  • +
+```````````````````````````````` + +https://github.com/pulldown-cmark/pulldown-cmark/issues/997 + +Link refdef split from paragraph with line with spaces. + +```````````````````````````````` example +[a]: /url + +: +. +

:

+```````````````````````````````` + + + +```````````````````````````````` example +[link]\[id] + +[id]: https://en.wikipedia.org +. +

[link][id]

+```````````````````````````````` + +```````````````````````````````` example +[id]\[] + +[id]: https://en.wikipedia.org +. +

id[]

+```````````````````````````````` + +https://github.com/pulldown-cmark/pulldown-cmark/issues/1099 + +Link must be separated from title by at least one space + +```````````````````````````````` example +[a](https://example.com"test") +[a]("test") +[a](https://example.com(test)) +[a]((test)) + +[a](https://example.com "test") +[a]( "test") +[a](https://example.com (test)) +[a]( (test)) +. +

a +[a](https://example.com"test") +a +[a](https://example.com(test))

+

a +a +a +a

+```````````````````````````````` diff --git a/pulldown-cmark/specs/strikethrough.txt b/pulldown-cmark/specs/strikethrough.txt index 73c8df6f..69831faa 100644 --- a/pulldown-cmark/specs/strikethrough.txt +++ b/pulldown-cmark/specs/strikethrough.txt @@ -122,4 +122,4 @@ The first one wins. ~This ~~is stricken~ but this is not~~ .

This ~~is stricken but this is not~~

-```````````````````````````````` +```````````````````````````````` \ No newline at end of file diff --git a/pulldown-cmark/specs/super_sub.txt b/pulldown-cmark/specs/super_sub.txt new file mode 100644 index 00000000..6da3dd61 --- /dev/null +++ b/pulldown-cmark/specs/super_sub.txt @@ -0,0 +1,115 @@ +# Superscript and subscript + +Basic strikethrough is between two tildes: + +```````````````````````````````` example_super_sub +^This is super^ ~This is sub~ +. +

This is super This is sub

+```````````````````````````````` + +```````````````````````````````` example_super_sub +~This is stricken out~ +. +

This is stricken out

+```````````````````````````````` + +Backslash escapes: + +```````````````````````````````` example_super_sub +~This is \~stricken~ +. +

This is ~stricken

+```````````````````````````````` + +```````````````````````````````` example_super_sub +~This~is~nothing~ +. +

Thisisnothing

+```````````````````````````````` + +```````````````````````````````` example_super_sub +~This ~~is not stricken.~ +. +

This ~~is not stricken.

+```````````````````````````````` + +```````````````````````````````` example_super_sub +~~This ~is~~ stricken.~ +. +

This ~is stricken.~

+```````````````````````````````` + +The first one wins. + +```````````````````````````````` example_super_sub +~This ~~is stricken~ but this is not~~ +. +

This ~~is stricken but this is not~~

+```````````````````````````````` + +Though strikethrough requires left and right flanking, subscript does not. +Neither does superscript. + +```````````````````````````````` example_super_sub +H~2~O + +y=x^2^a+xb+c +. +

H2O

+

y=x2a+xb+c

+```````````````````````````````` + +Superscript and subscript cannot group with a different delimiter count. + +```````````````````````````````` example_super_sub +~foo~~ + +^bar^^ +. +

~foo~~

+

^bar^^

+```````````````````````````````` + +The lower bound of superscript and subscript are separate. +Emphasis example included for analogy. + +```````````````````````````````` example_super_sub +~foo^~^bar~ + +*foo_*_bar* +. +

foo^^bar~

+

foo__bar*

+```````````````````````````````` + +Superscript with punctuation content (chemical ions, math). +When preceded by an alphanumeric character, superscript opens +even if the content is punctuation like `+` or `-`. + +```````````````````````````````` example_super_sub +H^+^ + OH^-^ +. +

H+ + OH-

+```````````````````````````````` + +```````````````````````````````` example_super_sub +Ca^2+^ + CO~3~^2-^ +. +

Ca2+ + CO32-

+```````````````````````````````` + +```````````````````````````````` example_super_sub +NH~4~^+^ +. +

NH4+

+```````````````````````````````` + +Standalone punctuation-only superscript without a preceding +alphanumeric does not open (avoids false positives). + +```````````````````````````````` example_super_sub +^+^ not superscript +. +

^+^ not superscript

+```````````````````````````````` diff --git a/pulldown-cmark/specs/wikilinks.txt b/pulldown-cmark/specs/wikilinks.txt new file mode 100644 index 00000000..0404346d --- /dev/null +++ b/pulldown-cmark/specs/wikilinks.txt @@ -0,0 +1,252 @@ +Run this with `cargo test --features gen-tests suite::wikilinks` + +# Wikilinks +Shorthand link definitions for use in Markdown-based wikis. Syntax and design +choices inspired heavily by +[Obsidian](https://obsidian.md/) and +[commonmark-hs](https://github.com/jgm/commonmark-hs). + +A wikilink is defined within double brackets. The destination url of the +resulting anchor is the text in the link. + +```````````````````````````````` example_wikilinks +This is a [[WikiLink]]. +. +

This is a WikiLink.

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +This is a [[Main/WikiLink]]. +. +

This is a Main/WikiLink.

+```````````````````````````````` + +Wikilinks take precedence over reference links: + +```````````````````````````````` example_wikilinks +This is [[Ambiguous]]. + +[Ambiguous]: https://example.com/ +. +

This is Ambiguous.

+```````````````````````````````` + +However, they should not otherwise interfere with normal Markdown links: + +```````````````````````````````` example_wikilinks +[[squid] calamari is considered a delicacy](https://en.wikipedia.org/wiki/Squid) + +[calamari [squid]](https://en.wikipedia.org/wiki/Squid) +. +

+[squid] calamari is considered a delicacy +

+

+calamari [squid] +

+```````````````````````````````` + +They also take precedence over inline links: + +```````````````````````````````` example_wikilinks +This is [also [[Ambiguous]]](https://example.com/). +. +

This is [also Ambiguous](https://example.com/).

+```````````````````````````````` + +Wikilinks, when enabled, may be used as an alternative to the autolink syntax; +\: + +```````````````````````````````` example_wikilinks + + +[[https://example.org/]] +. +

https://example.org/

+

https://example.org/

+```````````````````````````````` + +Wikilinks can have different display text through a utility called piping: + +```````````````````````````````` example_wikilinks +This is [[WikiLink|a pothole]]. +. +

This is a pothole.

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +This is a [[WikiLink/In/A/Directory|WikiLink]]. +. +

This is a WikiLink.

+```````````````````````````````` + +Using this syntax, it is possible to show more Markdown in the text: + +```````````````````````````````` example_wikilinks +This is [[WikiLink|a **strong** pothole]]. +. +

This is a strong pothole.

+```````````````````````````````` + +Or images: + +```````````````````````````````` example_wikilinks +This is a cute dog, linked to the page "WikiLink" + +[[WikiLink|![dog](dog.png)]] +. +

This is a cute dog, linked to the page "WikiLink"

+

+dog +

+```````````````````````````````` + +With nested wikilinks, the deepest one takes precedence: + +```````````````````````````````` example_wikilinks +[[WikiLink|[[Fish]]]] +. +

[[WikiLink|Fish]]

+```````````````````````````````` + +Links inside wikilinks will take precedence: + +```````````````````````````````` example_wikilinks +[[WikiLink|[cat](cat.html)]] +. +

[[WikiLink|cat]]

+```````````````````````````````` + +A similar looking syntax can be used to embed images: + +```````````````````````````````` example_wikilinks +This is a cute dog. + +![[dog.png]] +. +

This is a cute dog.

+

+dog.png +

+```````````````````````````````` + +In this syntax, the pipe operator serves as a way to define alt text. + +```````````````````````````````` example_wikilinks +![[dog.png|a cute dog]] +. +

a cute dog

+```````````````````````````````` + +Wikilinks cannot be empty. They will render as-is. + +```````````````````````````````` example_wikilinks +]] [[]] [[|]] [[|Symbol]] [[ +. +

]] [[]] [[|]] [[|Symbol]] [[

+```````````````````````````````` + +If the display text is empty then we omit it from the output anchor. + +```````````````````````````````` example_wikilinks +[[link|]] +. +

+```````````````````````````````` + +When a wikilink with a pipe separator is nested inside extra brackets, the +trailing `]]` after the wikilink must appear exactly once. + +```````````````````````````````` example_wikilinks +[[[[link|display]]]] +. +

[[display]]

+```````````````````````````````` + +Other interactions wikilinks have with other Markdown syntax: + +```````````````````````````````` example_wikilinks +[inline link]([[url]]) +. +

inline link

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +[inline link]([[url)]] +. +

inline link]]

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +`[[code]]` +. +

[[code]]

+```````````````````````````````` + +```````````````````````````````` example_wikilinks +emphasis **cross [[over** here]] +. +

emphasis **cross over** here

+```````````````````````````````` + +## Pipe escaping behavior +A pipe symbol can not be included in the href of a wikilink. That is to say the +pipe symbol cannot be escaped, and backslashes will be treated as part of the +URL. + +```````````````````````````````` example_wikilinks +[[first\|second]] +. +

second

+```````````````````````````````` + +Also, because the content within is taken as-is, HTML entities cannot be used +to get around this: + +```````````````````````````````` example_wikilinks +[[first!second]] +. +

first&#33;second

+```````````````````````````````` + +This is equivalent to the +[commonmark-hs](https://pandoc.org/try/?params=%7b%22text%22%3a%22%5b%5bfirst%5c%5c%7csecond%5d%5d%22%2c%22to%22%3a%22html5%22%2c%22from%22%3a%22commonmark%2bwikilinks_title_after_pipe%22%2c%22standalone%22%3afalse%2c%22embed-resources%22%3afalse%2c%22table-of-contents%22%3afalse%2c%22number-sections%22%3afalse%2c%22citeproc%22%3afalse%2c%22html-math-method%22%3a%22plain%22%2c%22wrap%22%3a%22auto%22%2c%22highlight-style%22%3anull%2c%22files%22%3a%7b%7d%2c%22template%22%3anull%7d) +behavior. + +This is also equivalent to [Gollum](https://github.com/gollum/gollum)'s +support for markdown it ships by default, though the order of href and +display text is switched. The above example roughly renders: + +```html +

first\

+``` + +Github Wiki, built on top of Gollum, similarly swaps the href and display text, +but the backslash **does not render at all**. The above example roughly +renders: + +```html +

first

+``` + +Obisidian, the original inspiration of this implementation, treats the +backslash as a path seperator. That is to say the backslash does not render +at all in the above example: + +```html +

second

+``` + +But the example `[[first\,third|second]]` renders to what +`[second](first/,third)` would on Obsidian. + +```html +

second

+``` + +On Obisidan Publish, the `[[first\,third|second]]` snippet *renders* +differently. With extra attributes and data omitted for clarity: + +```html +

+``` diff --git a/pulldown-cmark/src/firstpass.rs b/pulldown-cmark/src/firstpass.rs index 97c8bdbc..9873e300 100644 --- a/pulldown-cmark/src/firstpass.rs +++ b/pulldown-cmark/src/firstpass.rs @@ -1,23 +1,22 @@ //! The first pass resolves all block structure, generating an AST. Within a block, items //! are in a linear chain with potential inline markup identified. -use std::cmp::max; -use std::ops::Range; +use alloc::{string::String, vec::Vec}; +use core::{cmp::max, ops::Range, u8}; + +use unicase::UniCase; -use crate::parse::{ - scan_containers, Allocations, FootnoteDef, HeadingAttributes, Item, ItemBody, LinkDef, - LINK_MAX_NESTED_PARENS, -}; -use crate::strings::CowStr; -use crate::tree::{Tree, TreeIndex}; -use crate::Options; use crate::{ linklabel::{scan_link_label_rest, LinkLabel}, - HeadingLevel, + parse::{ + scan_containers, Allocations, FootnoteDef, HeadingAttributes, Item, ItemBody, LinkDef, + LINK_MAX_NESTED_PARENS, + }, + scanners::*, + strings::CowStr, + tree::{Tree, TreeIndex}, + ContainerKind, HeadingLevel, MetadataBlockKind, Options, }; -use crate::{scanners::*, MetadataBlockKind}; - -use unicase::UniCase; /// Runs the first pass, which resolves the block structure of the document, /// and returns the resulting tree. @@ -34,7 +33,6 @@ pub(crate) fn run_first_pass(text: &str, options: Options) -> (Tree, Alloc allocs: Allocations::new(), options, lookup_table, - next_paragraph_task: None, brace_context_next: 0, brace_context_stack: Vec::new(), }; @@ -61,9 +59,6 @@ struct FirstPass<'a, 'b> { allocs: Allocations<'a>, options: Options, lookup_table: &'b LookupTable, - /// This is `Some(item)` when the next paragraph - /// starts with a task list marker. - next_paragraph_task: Option, /// Math environment brace nesting. brace_context_stack: Vec, brace_context_next: usize, @@ -75,7 +70,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { while ix < self.text.len() { ix = self.parse_block(ix); } - for _ in 0..self.tree.spine_len() { + while self.tree.spine_len() > 0 { self.pop(ix); } (self.tree, self.allocs) @@ -90,11 +85,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.brace_context_stack.clear(); self.brace_context_next = 0; - let i = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let i = scan_containers(&self.tree, &mut line_start, self.options); for _ in i..self.tree.spine_len() { self.pop(start_ix); } @@ -108,47 +99,37 @@ impl<'a, 'b> FirstPass<'a, 'b> { } } } - - // Footnote definitions of the form - // [^bar]: - // * anything really - let container_start = start_ix + line_start.bytes_scanned(); - if let Some(bytecount) = self.parse_footnote(container_start) { - start_ix = container_start + bytecount; - start_ix += scan_blank_line(&bytes[start_ix..]).unwrap_or(0); - line_start = LineStart::new(&bytes[start_ix..]); - } } // Process new containers loop { - if self.options.has_gfm_footnotes() - || self.options.contains(Options::ENABLE_OLD_FOOTNOTES) - { - // Footnote definitions of the form - // [^bar]: - // * anything really - let save = line_start.clone(); - let indent = line_start.scan_space_upto(4); - if indent < 4 { - let container_start = start_ix + line_start.bytes_scanned(); - if let Some(bytecount) = self.parse_footnote(container_start) { - start_ix = container_start + bytecount; - line_start = LineStart::new(&bytes[start_ix..]); - continue; - } else { - line_start = save; + let save = line_start.clone(); + let outer_indent = line_start.scan_space_upto(4); + if outer_indent >= 4 { + line_start = save; + break; + } + if self.options.contains(Options::ENABLE_FOOTNOTES) { + // Footnote definitions + let container_start = start_ix + line_start.bytes_scanned(); + if let Some(bytecount) = self.parse_footnote(container_start) { + start_ix = container_start + bytecount; + if self.options.contains(Options::ENABLE_OLD_FOOTNOTES) { + // gfm footnotes need indented, but old footnotes don't + // handle this, old footnotes treat the next line as part of the current line + start_ix += scan_blank_line(&bytes[start_ix..]).unwrap_or(0); } - } else { - line_start = save; + line_start = LineStart::new(&bytes[start_ix..]); + continue; } } let container_start = start_ix + line_start.bytes_scanned(); - if let Some((ch, index, indent)) = line_start.scan_list_marker() { + if let Some((ch, index, indent)) = line_start.scan_list_marker_with_indent(outer_indent) + { let after_marker_index = start_ix + line_start.bytes_scanned(); - self.continue_list(container_start, ch, index); + self.continue_list(container_start - outer_indent, ch, index); self.tree.append(Item { - start: container_start, + start: container_start - outer_indent, end: after_marker_index, // will get updated later if item not empty body: ItemBody::ListItem(indent), }); @@ -170,10 +151,81 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.begin_list_item = Some(task_list_marker.end + n); return task_list_marker.end + n; } else { - self.next_paragraph_task = Some(task_list_marker); + line_start.scan_all_space(); + let ix = start_ix + line_start.bytes_scanned(); + return self.parse_paragraph(ix, Some(task_list_marker)); + } + } + } + } else if let Some((indent, child, item)) = self + .options + .contains(Options::ENABLE_DEFINITION_LIST) + .then(|| { + self.tree + .cur() + .map(|cur| (self.tree[cur].child, &mut self.tree[cur].item)) + }) + .flatten() + .filter(|(_, item)| { + matches!( + item, + Item { + body: ItemBody::Paragraph + | ItemBody::TightParagraph + | ItemBody::MaybeDefinitionListTitle + | ItemBody::DefinitionListDefinition(_), + .. } + ) + }) + .and_then(|item| { + Some(( + line_start + .scan_definition_list_definition_marker_with_indent(outer_indent)?, + item.0, + item.1, + )) + }) + { + match item.body { + ItemBody::Paragraph | ItemBody::TightParagraph => { + item.body = ItemBody::DefinitionList(true); + let Item { start, end, .. } = *item; + let list_idx = self.tree.cur().unwrap(); + let title_idx = self.tree.create_node(Item { + start, + end, // will get updated later if item not empty + body: ItemBody::DefinitionListTitle, + }); + self.tree[title_idx].child = child; + self.tree[list_idx].child = Some(title_idx); + self.tree.push(); + } + ItemBody::MaybeDefinitionListTitle => { + item.body = ItemBody::DefinitionListTitle; + } + ItemBody::DefinitionListDefinition(_) => {} + _ => unreachable!(), + } + let after_marker_index = start_ix + line_start.bytes_scanned(); + self.tree.append(Item { + start: container_start - outer_indent, + end: after_marker_index, // will get updated later if item not empty + body: ItemBody::DefinitionListDefinition(indent), + }); + if let Some(ItemBody::DefinitionList(ref mut is_tight)) = + self.tree.peek_up().map(|cur| &mut self.tree[cur].item.body) + { + if self.last_line_blank { + *is_tight = false; + self.last_line_blank = false; } } + self.tree.push(); + if let Some(n) = scan_blank_line(&bytes[after_marker_index..]) { + self.begin_list_item = Some(after_marker_index + n); + return after_marker_index + n; + } } else if line_start.scan_blockquote_marker() { let kind = if self.options.contains(Options::ENABLE_GFM) { line_start.scan_blockquote_tag() @@ -193,15 +245,14 @@ impl<'a, 'b> FirstPass<'a, 'b> { // and break out if we can't re-scan all of them let ix = start_ix + line_start.bytes_scanned(); let mut lazy_line_start = LineStart::new(&bytes[ix..]); - let current_container = scan_containers( - &self.tree, - &mut lazy_line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = + scan_containers(&self.tree, &mut lazy_line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); if !lazy_line_start.scan_space(4) && self.scan_paragraph_interrupt( &bytes[ix + lazy_line_start.bytes_scanned()..], current_container, + tree_position, ) { return ix; @@ -214,18 +265,108 @@ impl<'a, 'b> FirstPass<'a, 'b> { break; } } + } else if self.options.contains(Options::ENABLE_CONTAINER_EXTENSIONS) + && scan_ch_repeat(&bytes[(start_ix + line_start.bytes_scanned())..], b':') > 2 + { + let fence_length = scan_while_max( + &bytes[(start_ix + line_start.bytes_scanned())..], + |c| c == b':', + u8::MAX as usize, + ); + if self.tree.spine_len() > u8::MAX as usize { + break; + } else { + let excess_colons = scan_while( + &bytes[(start_ix + line_start.bytes_scanned() + fence_length)..], + |c| c == b':', + ); + let mut kind_start = + start_ix + line_start.bytes_scanned() + fence_length + excess_colons; + kind_start += scan_whitespace_no_nl(&bytes[kind_start..]); + let kind_length = scan_while(&bytes[kind_start..], |c| { + is_ascii_alphanumeric(c) || c == b'_' || c == b'-' || c == b':' || c == b'.' + }); + if kind_length == 0 { + break; + } else { + let kind = unescape( + &self.text[kind_start..(kind_start + kind_length)], + self.tree.is_in_table(), + ); + let mut summary_start = kind_start + kind_length; + summary_start += scan_whitespace_no_nl(&bytes[summary_start..]); + let line_end = summary_start + scan_nextline(&bytes[summary_start..]); + let summary_end = line_end + - scan_rev_while(&bytes[summary_start..line_end], is_ascii_whitespace); + if kind.eq_ignore_ascii_case("spoiler") { + let summary = unescape( + &self.text[summary_start..summary_end], + self.tree.is_in_table(), + ); + let summary_cow_ix = self.allocs.allocate_cow(summary); + self.tree.append(Item { + start: container_start, + end: 0, + body: ItemBody::Container( + fence_length as u8, + ContainerKind::Spoiler, + summary_cow_ix, + ), + }); + } else { + let kind_cow_ix = self.allocs.allocate_cow(kind); + self.tree.append(Item { + start: container_start, + end: 0, + body: ItemBody::Container( + fence_length as u8, + ContainerKind::Default, + kind_cow_ix, + ), + }); + } + self.tree.push(); + return summary_end + 1; + } + } } else { + line_start = save; break; } } + if self.options.contains(Options::ENABLE_CONTAINER_EXTENSIONS) { + let mut pop_count = None; + for (i, &node_ix) in self.tree.walk_spine().rev().enumerate() { + match self.tree[node_ix].item.body { + ItemBody::Container(length, ..) => { + if line_start.scan_closing_container_extensions_fence(length) { + pop_count = Some(i + 1); + break; + } + } + _ => { + break; + } + } + } + + if let Some(c) = pop_count { + for _ in 0..c { + self.pop(start_ix); + } + } + } let ix = start_ix + line_start.bytes_scanned(); if let Some(n) = scan_blank_line(&bytes[ix..]) { if let Some(node_ix) = self.tree.peek_up() { match &mut self.tree[node_ix].item.body { + ItemBody::Container(..) => (), ItemBody::BlockQuote(..) => (), - ItemBody::ListItem(indent) if self.begin_list_item.is_some() => { + ItemBody::ListItem(indent) | ItemBody::DefinitionListDefinition(indent) + if self.begin_list_item.is_some() => + { self.last_line_blank = true; // This is a blank list item. // While the list itself can be continued no matter how many blank lines @@ -237,17 +378,18 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.last_line_blank = true; } } + } else { + self.last_line_blank = true; } return ix + n; } - self.finish_list(start_ix); - // Save `remaining_space` here to avoid needing to backtrack `line_start` for HTML blocks let remaining_space = line_start.remaining_space(); let indent = line_start.scan_space_upto(4); if indent == 4 { + self.finish_list(start_ix); let ix = start_ix + line_start.bytes_scanned(); let remaining_space = line_start.remaining_space(); return self.parse_indented_code_block(ix, remaining_space); @@ -264,6 +406,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.options .contains(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS), ) { + self.finish_list(start_ix); return self.parse_metadata_block(ix, metadata_block_ch); } } @@ -273,6 +416,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { // Types 1-5 are all detected by one function and all end with the same // pattern if let Some(html_end_tag) = get_html_end_tag(&bytes[(ix + 1)..]) { + self.finish_list(start_ix); return self.parse_html_block_type_1_to_5( ix, html_end_tag, @@ -283,24 +427,29 @@ impl<'a, 'b> FirstPass<'a, 'b> { // Detect type 6 if starts_html_block_type_6(&bytes[(ix + 1)..]) { + self.finish_list(start_ix); return self.parse_html_block_type_6_or_7(ix, remaining_space, indent); } // Detect type 7 if let Some(_html_bytes) = scan_html_type_7(&bytes[ix..]) { + self.finish_list(start_ix); return self.parse_html_block_type_6_or_7(ix, remaining_space, indent); } } if let Ok(n) = scan_hrule(&bytes[ix..]) { + self.finish_list(start_ix); return self.parse_hrule(n, ix); } if let Some(atx_size) = scan_atx_heading(&bytes[ix..]) { + self.finish_list(start_ix); return self.parse_atx_heading(ix, atx_size); } if let Some((n, fence_ch)) = scan_code_fence(&bytes[ix..]) { + self.finish_list(start_ix); return self.parse_fenced_code_block(ix, indent, fence_ch, n); } @@ -328,32 +477,49 @@ impl<'a, 'b> FirstPass<'a, 'b> { // ``` if let Some(nl) = scan_blank_line(&bytes[ix..]) { ix += nl; - let mut lazy_line_start = LineStart::new(&bytes[ix..]); - let current_container = scan_containers( - &self.tree, - &mut lazy_line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); - if !lazy_line_start.scan_space(4) - && self.scan_paragraph_interrupt( - &bytes[ix + lazy_line_start.bytes_scanned()..], - current_container, - ) - { - return ix; - } else { - line_start = lazy_line_start; - line_start.scan_all_space(); - start_ix = ix; - } } else { + self.finish_list(start_ix); + return ix; + } + if let Some(lazy_line_start) = self.scan_next_line_or_lazy_continuation(&bytes[ix..]) { + line_start = lazy_line_start; + start_ix = ix; + } else { + self.finish_list(start_ix); return ix; } } let ix = start_ix + line_start.bytes_scanned(); - self.parse_paragraph(ix) + self.parse_paragraph(ix, None) + } + + /// footnote definitions and GFM quote markers can be "interrupted" + /// like paragraphs, but otherwise can't have other blocks after them. + /// + /// Call this at the end of the line to parse that. If it succeeeds, + /// this returns the LineStart for the new line. + fn scan_next_line_or_lazy_continuation<'input>( + &mut self, + bytes: &'input [u8], + ) -> Option> { + let mut line_start = LineStart::new(bytes); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); + if (!line_start.scan_space(4) + && self.scan_paragraph_interrupt( + &bytes[line_start.bytes_scanned()..], + current_container, + tree_position, + )) + || scan_blank_line(&bytes[line_start.bytes_scanned()..]).is_some() + { + None + } else { + line_start.scan_all_space(); + Some(line_start) + } } /// Returns the offset of the first line after the table. @@ -477,11 +643,8 @@ impl<'a, 'b> FirstPass<'a, 'b> { ) -> Option<(usize, TreeIndex)> { let bytes = self.text.as_bytes(); let mut line_start = LineStart::new(&bytes[ix..]); - let current_container = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); if !current_container { return None; } @@ -491,7 +654,9 @@ impl<'a, 'b> FirstPass<'a, 'b> { &bytes[ix..], current_container, self.options.contains(Options::ENABLE_FOOTNOTES), + self.options.contains(Options::ENABLE_DEFINITION_LIST), &self.tree, + tree_position, ) { return None; } @@ -501,17 +666,36 @@ impl<'a, 'b> FirstPass<'a, 'b> { } /// Returns offset of line start after paragraph. - fn parse_paragraph(&mut self, start_ix: usize) -> usize { + fn parse_paragraph(&mut self, start_ix: usize, tasklist_marker: Option) -> usize { + let body = if let Some(ItemBody::DefinitionList(_)) = + self.tree.peek_up().map(|idx| self.tree[idx].item.body) + { + if self.tree.cur().map_or(true, |idx| { + matches!( + &self.tree[idx].item.body, + ItemBody::DefinitionListDefinition(..) + ) + }) { + // blank lines between the previous definition and this one don't count + self.last_line_blank = false; + ItemBody::MaybeDefinitionListTitle + } else { + self.finish_list(start_ix); + ItemBody::Paragraph + } + } else { + self.finish_list(start_ix); + ItemBody::Paragraph + }; let node_ix = self.tree.append(Item { start: start_ix, end: 0, // will get set later - body: ItemBody::Paragraph, + body, }); self.tree.push(); - if let Some(item) = self.next_paragraph_task { + if let Some(item) = tasklist_marker { self.tree.append(item); - self.next_paragraph_task = None; } let bytes = self.text.as_bytes(); @@ -536,6 +720,9 @@ impl<'a, 'b> FirstPass<'a, 'b> { // be a cleaner way self.tree[node_ix].child = None; self.tree.pop(); + if body == ItemBody::MaybeDefinitionListTitle { + self.finish_list(ix); + } self.tree.push(); if let Some(ix) = self.parse_table(table_cols, ix, next_ix) { return ix; @@ -544,11 +731,9 @@ impl<'a, 'b> FirstPass<'a, 'b> { ix = next_ix; let mut line_start = LineStart::new(&bytes[ix..]); - let current_container = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); + let trailing_backslash_pos = match brk { Some(Item { start, @@ -566,18 +751,27 @@ impl<'a, 'b> FirstPass<'a, 'b> { if let Some(pos) = trailing_backslash_pos { self.tree.append_text(pos, pos + 1, false); } - ix = ix_setext; - break; + self.pop(ix_setext); + if body == ItemBody::MaybeDefinitionListTitle { + self.finish_list(ix); + } + return ix_setext; } } // first check for non-empty lists, then for other interrupts let suffix = &bytes[ix_new..]; - if self.scan_paragraph_interrupt(suffix, current_container) { + if self.scan_paragraph_interrupt(suffix, current_container, tree_position) { if let Some(pos) = trailing_backslash_pos { self.tree.append_text(pos, pos + 1, false); } break; } + if self.options.contains(Options::ENABLE_CONTAINER_EXTENSIONS) && !current_container + { + if line_start.scan_closing_container_extensions_fence(3) { + break; + } + } } line_start.scan_all_space(); if line_start.is_at_eol() { @@ -586,6 +780,28 @@ impl<'a, 'b> FirstPass<'a, 'b> { } break; } + + if self.options.contains(Options::ENABLE_CONTAINER_EXTENSIONS) { + let mut closes = false; + for &node_ix in self.tree.walk_spine().rev().skip(1) { + match self.tree[node_ix].item.body { + ItemBody::Container(length, ..) => { + if line_start.scan_closing_container_extensions_fence(length) { + closes = true; + break; + } + } + _ => { + break; + } + } + } + + if closes { + break; + } + } + ix = next_ix + line_start.bytes_scanned(); if let Some(item) = brk { self.tree.append(item); @@ -624,8 +840,27 @@ impl<'a, 'b> FirstPass<'a, 'b> { let new_end = if has_trailing_content { content_end } else { - let trailing_ws = - scan_rev_while(&bytes[header_start..content_end], is_ascii_whitespace_no_nl); + let mut last_line_start = header_start; + if attrs.is_some() { + loop { + let next_line_start = + last_line_start + scan_nextline(&bytes[last_line_start..content_end]); + if next_line_start >= content_end { + break; + } + let mut line_start = LineStart::new(&bytes[next_line_start..content_end]); + if scan_containers(&self.tree, &mut line_start, self.options) + != self.tree.spine_len() + { + break; + } + last_line_start = next_line_start + line_start.bytes_scanned(); + } + } + let trailing_ws = scan_rev_while( + &bytes[last_line_start..content_end], + is_ascii_whitespace_no_nl, + ); content_end - trailing_ws }; @@ -697,11 +932,8 @@ impl<'a, 'b> FirstPass<'a, 'b> { // check if we may be parsing a table let next_line_ix = ix + eol_bytes; let mut line_start = LineStart::new(&bytes[next_line_ix..]); - if scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len() + if scan_containers(&self.tree, &mut line_start, self.options) + == self.tree.spine_len() { let table_head_ix = next_line_ix + line_start.bytes_scanned(); let (table_head_bytes, alignment) = @@ -758,48 +990,49 @@ impl<'a, 'b> FirstPass<'a, 'b> { }), ) } - b'\\' => { - if ix + 1 < bytes_len && is_ascii_punctuation(bytes[ix + 1]) { - self.tree.append_text(begin_text, ix, backslash_escaped); - if bytes[ix + 1] == b'`' { - let count = 1 + scan_ch_repeat(&bytes[(ix + 2)..], b'`'); - self.tree.append(Item { - start: ix + 1, - end: ix + count + 1, - body: ItemBody::MaybeCode(count, true), - }); - begin_text = ix + 1 + count; - backslash_escaped = false; - LoopInstruction::ContinueAndSkip(count) - } else if bytes[ix + 1] == b'|' && TableParseMode::Active == mode { - // Yeah, it's super weird that backslash escaped pipes in tables aren't "real" - // backslash escapes. - // - // This tree structure is intended for the benefit of inline analysis, and it - // is supposed to operate as-if backslash escaped pipes were stripped out in a - // separate pass. - begin_text = ix + 1; - backslash_escaped = false; - LoopInstruction::ContinueAndSkip(1) - } else if ix + 2 < bytes_len - && bytes[ix + 1] == b'\\' - && bytes[ix + 2] == b'|' - && TableParseMode::Active == mode - { - // To parse `\\|`, discard the backslashes and parse the `|` that follows it. - begin_text = ix + 2; - backslash_escaped = true; - LoopInstruction::ContinueAndSkip(2) - } else { - begin_text = ix + 1; - backslash_escaped = true; - LoopInstruction::ContinueAndSkip(1) - } + b'\\' + if bytes + .get(ix + 1) + .copied() + .map_or(false, is_ascii_punctuation) => + { + self.tree.append_text(begin_text, ix, backslash_escaped); + if bytes[ix + 1] == b'`' { + let count = 1 + scan_ch_repeat(&bytes[(ix + 2)..], b'`'); + self.tree.append(Item { + start: ix + 1, + end: ix + count + 1, + body: ItemBody::MaybeCode(count, true), + }); + begin_text = ix + 1 + count; + backslash_escaped = false; + LoopInstruction::ContinueAndSkip(count) + } else if bytes[ix + 1] == b'|' && TableParseMode::Active == mode { + // Yeah, it's super weird that backslash escaped pipes in tables aren't "real" + // backslash escapes. + // + // This tree structure is intended for the benefit of inline analysis, and it + // is supposed to operate as-if backslash escaped pipes were stripped out in a + // separate pass. + begin_text = ix + 1; + backslash_escaped = false; + LoopInstruction::ContinueAndSkip(1) + } else if ix + 2 < bytes_len + && bytes[ix + 1] == b'\\' + && bytes[ix + 2] == b'|' + && TableParseMode::Active == mode + { + // To parse `\\|`, discard the backslashes and parse the `|` that follows it. + begin_text = ix + 2; + backslash_escaped = true; + LoopInstruction::ContinueAndSkip(2) } else { - LoopInstruction::ContinueAndSkip(0) + begin_text = ix + 1; + backslash_escaped = true; + LoopInstruction::ContinueAndSkip(1) } } - c @ b'*' | c @ b'_' | c @ b'~' => { + c @ b'*' | c @ b'_' | c @ b'~' | c @ b'^' | c @ b'=' => { let string_suffix = &self.text[ix..]; let count = 1 + scan_ch_repeat(&string_suffix.as_bytes()[1..], c); let can_open = delim_run_can_open( @@ -808,6 +1041,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { count, ix - start, mode, + self.options, ); let can_close = delim_run_can_close( &self.text[start..], @@ -815,8 +1049,13 @@ impl<'a, 'b> FirstPass<'a, 'b> { count, ix - start, mode, + self.options, ); - let is_valid_seq = c != b'~' || count <= 2; + let is_valid_seq = match c { + b'~' => count <= 2, + b'=' => count == 2, + _ => true, + }; if (can_open || can_close) && is_valid_seq { self.tree.append_text(begin_text, ix, backslash_escaped); @@ -833,18 +1072,17 @@ impl<'a, 'b> FirstPass<'a, 'b> { LoopInstruction::ContinueAndSkip(count - 1) } b'$' => { - let string_suffix = &self.text[ix..]; - let can_open = !string_suffix[1..] - .as_bytes() + let byte_suffix = &bytes[ix..]; + let can_open = !byte_suffix[1..] .first() .copied() .map_or(true, is_ascii_whitespace); let can_close = ix > start - && !self.text[..ix] - .as_bytes() + && !bytes[..ix] .last() .copied() - .map_or(true, is_ascii_whitespace); + .map_or(true, is_ascii_whitespace) + && !bytes.get(ix + 1).copied().map_or(false, is_ascii_numeric); // 0xFFFF_FFFF... represents the root brace context. Using None would require // storing Option, which is bigger than u8. @@ -944,20 +1182,16 @@ impl<'a, 'b> FirstPass<'a, 'b> { begin_text = ix + 1; LoopInstruction::ContinueAndSkip(0) } - b'!' => { - if ix + 1 < bytes_len && bytes[ix + 1] == b'[' { - self.tree.append_text(begin_text, ix, backslash_escaped); - backslash_escaped = false; - self.tree.append(Item { - start: ix, - end: ix + 2, - body: ItemBody::MaybeImage, - }); - begin_text = ix + 2; - LoopInstruction::ContinueAndSkip(1) - } else { - LoopInstruction::ContinueAndSkip(0) - } + b'!' if bytes.get(ix + 1) == Some(&b'[') => { + self.tree.append_text(begin_text, ix, backslash_escaped); + backslash_escaped = false; + self.tree.append(Item { + start: ix, + end: ix + 2, + body: ItemBody::MaybeImage, + }); + begin_text = ix + 2; + LoopInstruction::ContinueAndSkip(1) } b'[' => { self.tree.append_text(begin_text, ix, backslash_escaped); @@ -1006,20 +1240,16 @@ impl<'a, 'b> FirstPass<'a, 'b> { LoopInstruction::ContinueAndSkip(0) } } - b'.' => { - if ix + 2 < bytes.len() && bytes[ix + 1] == b'.' && bytes[ix + 2] == b'.' { - self.tree.append_text(begin_text, ix, backslash_escaped); - backslash_escaped = false; - self.tree.append(Item { - start: ix, - end: ix + 3, - body: ItemBody::SynthesizeChar('…'), - }); - begin_text = ix + 3; - LoopInstruction::ContinueAndSkip(2) - } else { - LoopInstruction::ContinueAndSkip(0) - } + b'.' if matches!(bytes.get(ix + 1..), Some(&[b'.', b'.', ..])) => { + self.tree.append_text(begin_text, ix, backslash_escaped); + backslash_escaped = false; + self.tree.append(Item { + start: ix, + end: ix + 3, + body: ItemBody::SynthesizeChar('…'), + }); + begin_text = ix + 3; + LoopInstruction::ContinueAndSkip(2) } b'-' => { let count = 1 + scan_ch_repeat(&bytes[(ix + 1)..], b'-'); @@ -1061,14 +1291,21 @@ impl<'a, 'b> FirstPass<'a, 'b> { } c @ b'\'' | c @ b'"' => { let string_suffix = &self.text[ix..]; - let can_open = - delim_run_can_open(&self.text[start..], string_suffix, 1, ix - start, mode); + let can_open = delim_run_can_open( + &self.text[start..], + string_suffix, + 1, + ix - start, + mode, + self.options, + ); let can_close = delim_run_can_close( &self.text[start..], string_suffix, 1, ix - start, mode, + self.options, ); self.tree.append_text(begin_text, ix, backslash_escaped); @@ -1127,11 +1364,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.append_html_line(remaining_space.max(indent), line_start_ix, ix); let mut line_start = LineStart::new(&bytes[ix..]); - let n_containers = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let n_containers = scan_containers(&self.tree, &mut line_start, self.options); if n_containers < self.tree.spine_len() { end_ix = ix; break; @@ -1180,11 +1413,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.append_html_line(remaining_space.max(indent), line_start_ix, ix); let mut line_start = LineStart::new(&bytes[ix..]); - let n_containers = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let n_containers = scan_containers(&self.tree, &mut line_start, self.options); if n_containers < self.tree.spine_len() || line_start.is_at_eol() { end_ix = ix; break; @@ -1231,11 +1460,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { } let mut line_start = LineStart::new(&bytes[ix..]); - let n_containers = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let n_containers = scan_containers(&self.tree, &mut line_start, self.options); if n_containers < self.tree.spine_len() || !(line_start.scan_space(4) || line_start.is_at_eol()) { @@ -1282,11 +1507,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.tree.push(); loop { let mut line_start = LineStart::new(&bytes[ix..]); - let n_containers = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let n_containers = scan_containers(&self.tree, &mut line_start, self.options); if n_containers < self.tree.spine_len() { // this line will get parsed again as not being part of the code // if it's blank, it should be parsed as a blank line @@ -1330,11 +1551,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { self.tree.push(); loop { let mut line_start = LineStart::new(&bytes[ix..]); - let n_containers = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ); + let n_containers = scan_containers(&self.tree, &mut line_start, self.options); if n_containers < self.tree.spine_len() { break; } @@ -1410,7 +1627,13 @@ impl<'a, 'b> FirstPass<'a, 'b> { fn pop(&mut self, ix: usize) { let cur_ix = self.tree.pop().unwrap(); self.tree[cur_ix].item.end = ix; - if let ItemBody::List(true, _, _) = self.tree[cur_ix].item.body { + if let ItemBody::DefinitionList(_) = self.tree[cur_ix].item.body { + fixup_end_of_definition_list(&mut self.tree, cur_ix); + self.begin_list_item = None; + } + if let ItemBody::List(true, _, _) | ItemBody::DefinitionList(true) = + self.tree[cur_ix].item.body + { surgerize_tight_list(&mut self.tree, cur_ix); self.begin_list_item = None; } @@ -1421,13 +1644,17 @@ impl<'a, 'b> FirstPass<'a, 'b> { fn finish_list(&mut self, ix: usize) { self.finish_empty_list_item(); if let Some(node_ix) = self.tree.peek_up() { - if let ItemBody::List(_, _, _) = self.tree[node_ix].item.body { + if let ItemBody::List(_, _, _) | ItemBody::DefinitionList(_) = + self.tree[node_ix].item.body + { self.pop(ix); } } if self.last_line_blank { if let Some(node_ix) = self.tree.peek_grandparent() { - if let ItemBody::List(ref mut is_tight, _, _) = self.tree[node_ix].item.body { + if let ItemBody::List(ref mut is_tight, _, _) + | ItemBody::DefinitionList(ref mut is_tight) = self.tree[node_ix].item.body + { *is_tight = false; } } @@ -1440,7 +1667,9 @@ impl<'a, 'b> FirstPass<'a, 'b> { if self.last_line_blank { // A list item can begin with at most one blank line. if let Some(node_ix) = self.tree.peek_up() { - if let ItemBody::ListItem(_) = self.tree[node_ix].item.body { + if let ItemBody::ListItem(_) | ItemBody::DefinitionListDefinition(_) = + self.tree[node_ix].item.body + { self.pop(begin_list_item); } } @@ -1602,7 +1831,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { return None; } i += 2; - if scan_ch(&bytes[i..], b':') == 0 { + if bytes.get(i) != Some(&b':') { return None; } i += 1; @@ -1616,10 +1845,10 @@ impl<'a, 'b> FirstPass<'a, 'b> { if self.options.has_gfm_footnotes() { i += scan_whitespace_no_nl(&bytes[i..]); } - self.allocs.footdefs.0.insert( - UniCase::new(label.clone()), - FootnoteDef { use_count: 0 }, - ); + self.allocs + .footdefs + .0 + .insert(UniCase::new(label.clone()), FootnoteDef { use_count: 0 }); self.tree.append(Item { start, end: 0, // will get set later @@ -1637,17 +1866,14 @@ impl<'a, 'b> FirstPass<'a, 'b> { &self.text[start..], &|bytes| { let mut line_start = LineStart::new(bytes); - let current_container = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); if line_start.scan_space(4) { return Some(line_start.bytes_scanned()); } let bytes_scanned = line_start.bytes_scanned(); let suffix = &bytes[bytes_scanned..]; - if self.scan_paragraph_interrupt(suffix, current_container) + if self.scan_paragraph_interrupt(suffix, current_container, tree_position) || (current_container && scan_setext_heading(suffix).is_some()) { None @@ -1662,12 +1888,12 @@ impl<'a, 'b> FirstPass<'a, 'b> { /// Returns number of bytes scanned, label and definition on success. fn parse_refdef_total(&mut self, start: usize) -> Option<(usize, LinkLabel<'a>, LinkDef<'a>)> { let bytes = &self.text.as_bytes()[start..]; - if scan_ch(bytes, b'[') == 0 { + if bytes.get(0) != Some(&b'[') { return None; } let (mut i, label) = self.parse_refdef_label(start + 1)?; i += 1; - if scan_ch(&bytes[i..], b':') == 0 { + if bytes.get(i) != Some(&b':') { return None; } i += 1; @@ -1691,14 +1917,11 @@ impl<'a, 'b> FirstPass<'a, 'b> { break; } let mut line_start = LineStart::new(&bytes[i..]); - let current_container = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); if !line_start.scan_space(4) { let suffix = &bytes[i + line_start.bytes_scanned()..]; - if self.scan_paragraph_interrupt(suffix, current_container) + if self.scan_paragraph_interrupt(suffix, current_container, tree_position) || scan_setext_heading(suffix).is_some() { return None; @@ -1753,14 +1976,11 @@ impl<'a, 'b> FirstPass<'a, 'b> { bytecount += 1; } let mut line_start = LineStart::new(&bytes[bytecount..]); - let current_container = scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) == self.tree.spine_len(); + let tree_position = scan_containers(&self.tree, &mut line_start, self.options); + let current_container = tree_position == self.tree.spine_len(); if !line_start.scan_space(4) { let suffix = &bytes[bytecount + line_start.bytes_scanned()..]; - if self.scan_paragraph_interrupt(suffix, current_container) + if self.scan_paragraph_interrupt(suffix, current_container, tree_position) || scan_setext_heading(suffix).is_some() { return None; @@ -1861,9 +2081,20 @@ impl<'a, 'b> FirstPass<'a, 'b> { } /// Checks whether we should break a paragraph on the given input. - fn scan_paragraph_interrupt(&self, bytes: &[u8], current_container: bool) -> bool { - let has_footnote = self.options.contains(Options::ENABLE_FOOTNOTES); - if scan_paragraph_interrupt_no_table(bytes, current_container, has_footnote, &self.tree) { + fn scan_paragraph_interrupt( + &self, + bytes: &[u8], + current_container: bool, + tree_position: usize, + ) -> bool { + if scan_paragraph_interrupt_no_table( + bytes, + current_container, + self.options.contains(Options::ENABLE_FOOTNOTES), + self.options.contains(Options::ENABLE_DEFINITION_LIST), + &self.tree, + tree_position, + ) { return true; } // pulldown-cmark allows heavy tables, that have a `|` on the header row, @@ -1927,12 +2158,7 @@ impl<'a, 'b> FirstPass<'a, 'b> { // ^ // | need to skip over the `>` when checking for the table let mut line_start = LineStart::new(&bytes[next_line_ix..]); - if scan_containers( - &self.tree, - &mut line_start, - self.options.has_gfm_footnotes(), - ) != self.tree.spine_len() - { + if scan_containers(&self.tree, &mut line_start, self.options) != self.tree.spine_len() { return false; } let table_head_ix = next_line_ix + line_start.bytes_scanned(); @@ -2019,12 +2245,15 @@ fn scan_paragraph_interrupt_no_table( bytes: &[u8], current_container: bool, has_footnote: bool, + definition_list: bool, tree: &Tree, + tree_position: usize, ) -> bool { scan_eol(bytes).is_some() || scan_hrule(bytes).is_ok() || scan_atx_heading(bytes).is_some() || scan_code_fence(bytes).is_some() + || scan_interrupting_container_extensions_fence(bytes) || scan_blockquote_start(bytes).is_some() || scan_listitem(bytes).map_or(false, |(ix, delim, index, _)| { ! current_container || @@ -2036,10 +2265,24 @@ fn scan_paragraph_interrupt_no_table( }) || bytes.starts_with(b"<") && (get_html_end_tag(&bytes[1..]).is_some() || starts_html_block_type_6(&bytes[1..])) + || definition_list + && ((current_container + && tree.peek_up().map_or(false, |cur| { + matches!( + tree[cur].item.body, + ItemBody::Paragraph + | ItemBody::TightParagraph + | ItemBody::MaybeDefinitionListTitle + ) + })) + || tree.walk_spine().nth(tree_position).map_or(false, |cur| { + matches!(tree[*cur].item.body, ItemBody::DefinitionListDefinition(_)) + })) + && bytes.starts_with(b":") || (has_footnote && bytes.starts_with(b"[^") && scan_link_label_rest( - std::str::from_utf8(&bytes[2..]).unwrap(), + core::str::from_utf8(&bytes[2..]).unwrap(), &|_| None, tree.is_in_table(), ) @@ -2091,47 +2334,39 @@ fn get_html_end_tag(text_bytes: &[u8]) -> Option<&'static str> { } } -// https://english.stackexchange.com/a/285573 fn surgerize_tight_list(tree: &mut Tree, list_ix: TreeIndex) { let mut list_item = tree[list_ix].child; while let Some(listitem_ix) = list_item { - // first child is special, controls how we repoint list_item.child - let list_item_firstborn = tree[listitem_ix].child; - - // Check that list item has children - this is not necessarily the case! - if let Some(firstborn_ix) = list_item_firstborn { - if let ItemBody::Paragraph = tree[firstborn_ix].item.body { - tree[listitem_ix].child = tree[firstborn_ix].child; + let mut node_ix = tree[listitem_ix].child; + while let Some(node) = node_ix { + if let ItemBody::Paragraph = tree[node].item.body { + tree[node].item.body = ItemBody::TightParagraph; } + node_ix = tree[node].next; + } - let mut list_item_child = Some(firstborn_ix); - let mut node_to_repoint = None; - while let Some(child_ix) = list_item_child { - // surgerize paragraphs - let repoint_ix = if let ItemBody::Paragraph = tree[child_ix].item.body { - if let Some(child_firstborn) = tree[child_ix].child { - if let Some(repoint_ix) = node_to_repoint { - tree[repoint_ix].next = Some(child_firstborn); - } - let mut child_lastborn = child_firstborn; - while let Some(lastborn_next_ix) = tree[child_lastborn].next { - child_lastborn = lastborn_next_ix; - } - child_lastborn - } else { - child_ix - } - } else { - child_ix - }; + list_item = tree[listitem_ix].next; + } +} - node_to_repoint = Some(repoint_ix); - tree[repoint_ix].next = tree[child_ix].next; - list_item_child = tree[child_ix].next; +fn fixup_end_of_definition_list(tree: &mut Tree, list_ix: TreeIndex) { + let mut list_item = tree[list_ix].child; + let mut previous_list_item = None; + while let Some(listitem_ix) = list_item { + match &mut tree[listitem_ix].item.body { + ItemBody::DefinitionListTitle | ItemBody::DefinitionListDefinition(_) => { + previous_list_item = list_item; + list_item = tree[listitem_ix].next; } + body @ ItemBody::MaybeDefinitionListTitle => { + *body = ItemBody::Paragraph; + break; + } + _ => break, } - - list_item = tree[listitem_ix].next; + } + if let Some(previous_list_item) = previous_list_item { + tree.truncate_to_parent(previous_list_item); } } @@ -2146,8 +2381,9 @@ fn delim_run_can_open( run_len: usize, ix: usize, mode: TableParseMode, + options: Options, ) -> bool { - let next_char = if let Some(c) = suffix.chars().nth(run_len) { + let next_char = if let Some(c) = suffix[run_len..].chars().next() { c } else { return false; @@ -2155,32 +2391,61 @@ fn delim_run_can_open( if next_char.is_whitespace() { return false; } + let delim = suffix.bytes().next().unwrap(); + // `^` with punctuation content (e.g. `^+^`) requires a preceding alphanumeric + // or delimiter character (e.g. `H^+^`, `NH~4~^+^`). At start of line (ix == 0) + // there is no preceding character, so `^` cannot open with punctuation content. + // This check must run before the `ix == 0` early return below. + if delim == b'^' && is_punctuation(next_char) { + if ix == 0 { + return false; + } + if options.contains(Options::ENABLE_SUPERSCRIPT) { + let prev_char = s[..ix].chars().last(); + // Allow after alphanumeric (H^+^) or after closing delimiters + // like ~ or ^ (NH~4~^+^, x^2^^+^) + if prev_char.is_some_and(|c| c.is_alphanumeric() || c == '~' || c == '^') { + return true; + } + } + // Punctuation content without qualifying predecessor -- don't open + return false; + } if ix == 0 { return true; } if mode == TableParseMode::Active { - if s[..ix].ends_with('|') && !s[..ix].ends_with(r"\|") { + if s.as_bytes()[..ix].ends_with(b"|") && !s.as_bytes()[..ix].ends_with(br"\|") { return true; } if next_char == '|' { return false; } } - let delim = suffix.chars().next().unwrap(); - // `*` and `~~` can be intraword, `_` and `~` cannot - if delim == '*' && !is_punctuation(next_char) { + // `*`, `~~`, and `^` can be intraword, `~` can only be interword if it's subscript, `_` cannot + if delim == b'*' && !is_punctuation(next_char) { + return true; + } + // `^` with non-punctuation content can open intraword + if delim == b'^' { + return true; + } + if delim == b'~' && run_len > 1 { return true; } - if delim == '~' && run_len > 1 { + if delim == b'=' && run_len == 2 { return true; } let prev_char = s[..ix].chars().last().unwrap(); - if delim == '~' && prev_char == '~' && !is_punctuation(next_char) { + if delim == b'~' + && (prev_char == '~' || options.contains(Options::ENABLE_SUBSCRIPT)) + && !is_punctuation(next_char) + { return true; } prev_char.is_whitespace() - || is_punctuation(prev_char) && (delim != '\'' || ![']', ')'].contains(&prev_char)) + || is_punctuation(prev_char) && (delim != b'\'' || ![']', ')'].contains(&prev_char)) } /// Determines whether the delimiter run starting at given index is @@ -2192,6 +2457,7 @@ fn delim_run_can_close( run_len: usize, ix: usize, mode: TableParseMode, + options: Options, ) -> bool { if ix == 0 { return false; @@ -2200,25 +2466,30 @@ fn delim_run_can_close( if prev_char.is_whitespace() { return false; } - let next_char = if let Some(c) = suffix.chars().nth(run_len) { + let next_char = if let Some(c) = suffix[run_len..].chars().next() { c } else { return true; }; if mode == TableParseMode::Active { - if s[..ix].ends_with('|') && !s[..ix].ends_with(r"\|") { + if s.as_bytes()[..ix].ends_with(b"|") && !s.as_bytes()[..ix].ends_with(br"\|") { return false; } if next_char == '|' { return true; } } - let delim = suffix.chars().next().unwrap(); - // `*` and `~~` can be intraword, `_` and `~` cannot - if (delim == '*' || (delim == '~' && run_len > 1)) && !is_punctuation(prev_char) { + let delim = suffix.bytes().next().unwrap(); + // `*`, `~~`, and `^` can be intraword, `~` can only be interword if it's subscript, `_` cannot + if (delim == b'*' || (delim == b'~' && run_len > 1) || (delim == b'=' && run_len == 2)) + && !is_punctuation(prev_char) + { + return true; + } + if delim == b'^' && !is_punctuation(prev_char) { return true; } - if delim == '~' && prev_char == '~' { + if delim == b'~' && (prev_char == '~' || options.contains(Options::ENABLE_SUBSCRIPT)) { return true; } @@ -2251,9 +2522,17 @@ fn special_bytes(options: &Options) -> [bool; 256] { if options.contains(Options::ENABLE_TABLES) { bytes[b'|' as usize] = true; } - if options.contains(Options::ENABLE_STRIKETHROUGH) { + if options.contains(Options::ENABLE_STRIKETHROUGH) + || options.contains(Options::ENABLE_SUBSCRIPT) + { bytes[b'~' as usize] = true; } + if options.contains(Options::ENABLE_SUPERSCRIPT) { + bytes[b'^' as usize] = true; + } + if options.contains(Options::ENABLE_HIGHLIGHT) { + bytes[b'=' as usize] = true; + } if options.contains(Options::ENABLE_MATH) { bytes[b'$' as usize] = true; bytes[b'{' as usize] = true; @@ -2285,8 +2564,13 @@ struct LookupTable { type LookupTable = [bool; 256]; /// This function walks the byte slices from the given index and -/// calls the callback function on all bytes (and their indices) that are in the following set: -/// `` ` ``, `\`, `&`, `*`, `_`, `~`, `!`, `<`, `[`, `]`, `|`, `\r`, `\n` +/// calls the callback function on all bytes (and their indices) that are in the +/// special-bytes set defined by [`special_bytes`]/[`simd::compute_lookup`]. +/// The always-included bytes are +/// `` ` ``, `\`, `&`, `*`, `_`, `!`, `<`, `[`, `]`, `\r`, `\n`; additional bytes +/// are added when their corresponding option is enabled (e.g. `|` with tables, +/// `~` with strikethrough/subscript, `^` with superscript, `=` with highlight, +/// `$`/`{`/`}` with math, and `.`/`-`/`"`/`'` with smart punctuation). /// It is guaranteed not call the callback on other bytes. /// Whenever `callback(ix, byte)` returns a `ContinueAndSkip(n)` value, the callback /// will not be called with an index that is less than `ix + n + 1`. @@ -2469,11 +2753,12 @@ mod simd { //! //! [great overview]: http://0x80.pl/articles/simd-byte-lookup.html + use core::arch::x86_64::*; + use super::{LookupTable, LoopInstruction}; use crate::Options; - use core::arch::x86_64::*; - const VECTOR_SIZE: usize = std::mem::size_of::<__m128i>(); + const VECTOR_SIZE: usize = core::mem::size_of::<__m128i>(); /// Generates a lookup table containing the bitmaps for our /// special marker bytes. This is effectively a 128 element 2d bitvector, @@ -2491,9 +2776,17 @@ mod simd { if options.contains(Options::ENABLE_TABLES) { add_lookup_byte(&mut lookup, b'|'); } - if options.contains(Options::ENABLE_STRIKETHROUGH) { + if options.contains(Options::ENABLE_STRIKETHROUGH) + || options.contains(Options::ENABLE_SUBSCRIPT) + { add_lookup_byte(&mut lookup, b'~'); } + if options.contains(Options::ENABLE_SUPERSCRIPT) { + add_lookup_byte(&mut lookup, b'^'); + } + if options.contains(Options::ENABLE_HIGHLIGHT) { + add_lookup_byte(&mut lookup, b'='); + } if options.contains(Options::ENABLE_MATH) { add_lookup_byte(&mut lookup, b'$'); add_lookup_byte(&mut lookup, b'{'); @@ -2594,7 +2887,11 @@ mod simd { match callback(offset, *bytes.get_unchecked(offset)) { LoopInstruction::ContinueAndSkip(skip) => { offset += skip + 1; - mask = mask.wrapping_shr((skip + 1 + mask_ix) as u32); + let shift = skip + 1 + mask_ix; + if shift >= 32 { + break; + } + mask >>= shift; } LoopInstruction::BreakAtWith(ix, val) => return Err((ix, val)), } @@ -2621,7 +2918,7 @@ mod simd { let mask = compute_mask(lut, bytes, ix); let block_start = ix; ix = match process_mask(mask, bytes, ix, &mut callback) { - Ok(ix) => std::cmp::max(ix, VECTOR_SIZE + block_start), + Ok(ix) => core::cmp::max(ix, VECTOR_SIZE + block_start), Err((end_ix, val)) => return (end_ix, val), }; } @@ -2639,8 +2936,7 @@ mod simd { #[cfg(test)] mod simd_test { - use super::super::create_lut; - use super::{iterate_special_bytes, LoopInstruction}; + use super::{super::create_lut, iterate_special_bytes, LoopInstruction}; use crate::Options; fn check_expected_indices(bytes: &[u8], expected: &[usize], skip: usize) { @@ -2649,6 +2945,7 @@ mod simd { opts.insert(Options::ENABLE_TABLES); opts.insert(Options::ENABLE_FOOTNOTES); opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_SUPERSCRIPT); opts.insert(Options::ENABLE_TASKLISTS); let lut = create_lut(&opts); @@ -2690,8 +2987,8 @@ mod simd { #[test] fn exhaustive_search() { let chars = [ - b'\n', b'\r', b'*', b'_', b'~', b'|', b'&', b'\\', b'[', b']', b'<', b'!', b'`', - b'$', b'{', b'}', + b'\n', b'\r', b'*', b'_', b'~', b'^', b'|', b'&', b'\\', b'[', b']', b'<', b'!', + b'`', b'$', b'{', b'}', ]; for &c in &chars { diff --git a/pulldown-cmark/src/html.rs b/pulldown-cmark/src/html.rs index 587ecba2..6960f0b4 100644 --- a/pulldown-cmark/src/html.rs +++ b/pulldown-cmark/src/html.rs @@ -20,13 +20,22 @@ //! HTML renderer that takes an iterator of events as input. +use alloc::{string::String, vec::Vec}; +#[cfg(all(feature = "std", not(feature = "hashbrown")))] use std::collections::HashMap; -use crate::strings::CowStr; -use crate::Event::*; -use crate::{Alignment, BlockQuoteKind, CodeBlockKind, Event, LinkType, Tag, TagEnd}; -use pulldown_cmark_escape::{ - escape_href, escape_html, escape_html_body_text, FmtWriter, IoWriter, StrWrite, +#[cfg(feature = "hashbrown")] +use hashbrown::HashMap; +#[cfg(feature = "std")] +use pulldown_cmark_escape::IoWriter; +use pulldown_cmark_escape::{escape_href, escape_html, escape_html_body_text, FmtWriter, StrWrite}; + +use crate::{ + strings::CowStr, + Alignment, BlockQuoteKind, CodeBlockKind, + ContainerKind::*, + Event::{self, *}, + LinkType, Tag, TagEnd, }; enum TableState { @@ -270,6 +279,26 @@ where CodeBlockKind::Indented => self.write("
"),
                 }
             }
+            Tag::ContainerBlock(Default, kind) => {
+                if !self.end_newline {
+                    self.write_newline()?;
+                }
+                self.write("
") + } + Tag::ContainerBlock(Spoiler, summary) => { + if !self.end_newline { + self.write_newline()?; + } + if summary.is_empty() { + self.write("
") + } else { + self.write("
")?; + escape_html(&mut self.writer, summary.as_ref())?; + self.write("") + } + } Tag::List(Some(1)) => { if self.end_newline { self.write("
    \n") @@ -300,9 +329,33 @@ where self.write("\n
  1. ") } } + Tag::DefinitionList => { + if self.end_newline { + self.write("
    \n") + } else { + self.write("\n
    \n") + } + } + Tag::DefinitionListTitle => { + if self.end_newline { + self.write("
    ") + } else { + self.write("\n
    ") + } + } + Tag::DefinitionListDefinition => { + if self.end_newline { + self.write("
    ") + } else { + self.write("\n
    ") + } + } + Tag::Subscript => self.write(""), + Tag::Superscript => self.write(""), Tag::Emphasis => self.write(""), Tag::Strong => self.write(""), Tag::Strikethrough => self.write(""), + Tag::Highlight => self.write(""), Tag::Link { link_type: LinkType::Email, dest_url, @@ -405,6 +458,12 @@ where TagEnd::CodeBlock => { self.write("
\n")?; } + TagEnd::ContainerBlock(Spoiler) => { + self.write("\n")?; + } + TagEnd::ContainerBlock(Default) => { + self.write("\n")?; + } TagEnd::List(true) => { self.write("\n")?; } @@ -414,15 +473,33 @@ where TagEnd::Item => { self.write("\n")?; } + TagEnd::DefinitionList => { + self.write("\n")?; + } + TagEnd::DefinitionListTitle => { + self.write("\n")?; + } + TagEnd::DefinitionListDefinition => { + self.write("\n")?; + } TagEnd::Emphasis => { self.write("")?; } + TagEnd::Superscript => { + self.write("")?; + } + TagEnd::Subscript => { + self.write("
")?; + } TagEnd::Strong => { self.write("")?; } TagEnd::Strikethrough => { self.write("")?; } + TagEnd::Highlight => { + self.write("
")?; + } TagEnd::Link => { self.write("")?; } @@ -549,6 +626,7 @@ where /// /// "#); /// ``` +#[cfg(feature = "std")] pub fn write_html_io<'a, I, W>(writer: W, iter: I) -> std::io::Result<()> where I: Iterator>, @@ -584,10 +662,10 @@ where /// /// "#); /// ``` -pub fn write_html_fmt<'a, I, W>(writer: W, iter: I) -> std::fmt::Result +pub fn write_html_fmt<'a, I, W>(writer: W, iter: I) -> core::fmt::Result where I: Iterator>, - W: std::fmt::Write, + W: core::fmt::Write, { HtmlWriter::new(iter, FmtWriter(writer)).run() } diff --git a/pulldown-cmark/src/lib.rs b/pulldown-cmark/src/lib.rs index 2fac8320..849b40fa 100644 --- a/pulldown-cmark/src/lib.rs +++ b/pulldown-cmark/src/lib.rs @@ -68,7 +68,11 @@ //! } //! ``` //! - +#![warn( + clippy::alloc_instead_of_core, + clippy::std_instead_of_alloc, + clippy::std_instead_of_core +)] // When compiled for the rustc compiler itself we want to make sure that this is // an unstable crate. #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] @@ -76,6 +80,18 @@ // Forbid unsafe code unless the SIMD feature is enabled. #![cfg_attr(not(feature = "simd"), forbid(unsafe_code))] #![warn(missing_debug_implementations)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(all(not(feature = "std"), not(feature = "hashbrown")))] +compile_error!("\"hashbrown\" feature should be enabled in \"no_std\" environment."); + +use alloc::vec::Vec; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -94,13 +110,16 @@ mod scanners; mod strings; mod tree; -use std::fmt::Display; +use core::fmt::Display; -pub use crate::parse::{ - BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback, OffsetIter, Parser, RefDefs, +pub use crate::{ + parse::{ + BrokenLink, BrokenLinkCallback, DefaultParserCallbacks, LinkDef, OffsetIter, Parser, + ParserCallbacks, RefDefs, + }, + strings::{CowStr, InlineStr}, + utils::*, }; -pub use crate::strings::{CowStr, InlineStr}; -pub use crate::utils::*; /// Codeblock kind. #[derive(Clone, Debug, PartialEq)] @@ -120,6 +139,13 @@ impl<'a> CodeBlockKind<'a> { pub fn is_fenced(&self) -> bool { matches!(*self, CodeBlockKind::Fenced(_)) } + + pub fn into_static(self) -> CodeBlockKind<'static> { + match self { + CodeBlockKind::Indented => CodeBlockKind::Indented, + CodeBlockKind::Fenced(s) => CodeBlockKind::Fenced(s.into_static()), + } + } } /// BlockQuote kind (Note, Tip, Important, Warning, Caution). @@ -133,6 +159,14 @@ pub enum BlockQuoteKind { Caution, } +/// ContainerBlock kind (Spoiler only). +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ContainerKind { + Default, + Spoiler, +} + /// Metadata block kind. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -152,6 +186,8 @@ pub enum Tag<'a> { /// The identifier is prefixed with `#` and the last one in the attributes /// list is chosen, classes are prefixed with `.` and custom attributes /// have no prefix and can optionally have a value (`myattr` or `myattr=myvalue`). + /// + /// `id`, `classes` and `attrs` are only parsed and populated with [`Options::ENABLE_HEADING_ATTRIBUTES`], `None` or empty otherwise. Heading { level: HeadingLevel, id: Option>, @@ -160,11 +196,37 @@ pub enum Tag<'a> { attrs: Vec<(CowStr<'a>, Option>)>, }, + /// A block quote. + /// + /// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_GFM`], `None` otherwise. + /// + /// ```markdown + /// > regular quote + /// + /// > [!NOTE] + /// > note quote + /// ``` BlockQuote(Option), /// A code block. CodeBlock(CodeBlockKind<'a>), - - /// A HTML block. + ContainerBlock(ContainerKind, CowStr<'a>), + + /// An HTML block. + /// + /// A line that begins with some predefined tags (HTML block tags) (see [CommonMark Spec](https://spec.commonmark.org/0.31.2/#html-blocks) for more details) or any tag that is followed only by whitespace. + /// + /// Most HTML blocks end on an empty line, though some e.g. `
` like `1. *bar*
 "##;
 
-    test_markdown_html(original, expected, false, false, false);
+    test_markdown_html(original, expected, false, false, false, false, false, false, false);
 }
 
 #[test]
@@ -2471,7 +2471,7 @@ bar
 

okay

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2491,7 +2491,7 @@ okay

okay

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2501,7 +2501,7 @@ fn spec_test_181() { let expected = r##" "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2535,7 +2535,7 @@ function matchwo(a,b)

okay

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2549,7 +2549,7 @@ fn spec_test_183() {
"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2563,7 +2563,7 @@ fn spec_test_184() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2579,7 +2579,7 @@ bar "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2595,7 +2595,7 @@ bar *foo* "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2609,7 +2609,7 @@ baz baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2625,7 +2625,7 @@ fn spec_test_188() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2639,7 +2639,7 @@ fn spec_test_189() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2665,7 +2665,7 @@ Hi "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2692,7 +2692,7 @@ fn spec_test_191() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2704,7 +2704,7 @@ fn spec_test_192() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2718,7 +2718,7 @@ fn spec_test_193() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2730,7 +2730,7 @@ fn spec_test_194() { let expected = r##"

Foo*bar]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2744,7 +2744,7 @@ fn spec_test_195() { let expected = r##"

Foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2764,7 +2764,7 @@ line2 ">foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2780,7 +2780,7 @@ with blank line'

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2793,7 +2793,7 @@ fn spec_test_198() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2806,7 +2806,7 @@ fn spec_test_199() {

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2818,7 +2818,7 @@ fn spec_test_200() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2831,7 +2831,7 @@ fn spec_test_201() {

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2843,7 +2843,7 @@ fn spec_test_202() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2855,7 +2855,7 @@ fn spec_test_203() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2868,7 +2868,7 @@ fn spec_test_204() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2880,7 +2880,7 @@ fn spec_test_205() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2892,7 +2892,7 @@ fn spec_test_206() { let expected = r##"

αγω

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2901,7 +2901,7 @@ fn spec_test_207() { "##; let expected = r##""##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2914,7 +2914,7 @@ bar let expected = r##"

bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2924,7 +2924,7 @@ fn spec_test_209() { let expected = r##"

[foo]: /url "title" ok

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2935,7 +2935,7 @@ fn spec_test_210() { let expected = r##"

"title" ok

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2949,7 +2949,7 @@ fn spec_test_211() {

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2965,7 +2965,7 @@ fn spec_test_212() {

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2980,7 +2980,7 @@ fn spec_test_213() {

[bar]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -2995,7 +2995,7 @@ fn spec_test_214() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3009,7 +3009,7 @@ bar

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3022,7 +3022,7 @@ fn spec_test_216() { foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3041,7 +3041,7 @@ fn spec_test_217() { baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3055,7 +3055,7 @@ fn spec_test_218() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3068,7 +3068,7 @@ bbb

bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3085,7 +3085,7 @@ bbb

ddd

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3099,7 +3099,7 @@ bbb

bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3111,7 +3111,7 @@ fn spec_test_222() { bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3125,7 +3125,7 @@ bbb ccc

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3137,7 +3137,7 @@ bbb bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3150,7 +3150,7 @@ bbb

bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3162,7 +3162,7 @@ bbb bbb

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3180,7 +3180,7 @@ aaa

aaa

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3196,7 +3196,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3212,7 +3212,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3228,7 +3228,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3243,7 +3243,7 @@ fn spec_test_231() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3259,7 +3259,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3275,7 +3275,7 @@ foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3289,7 +3289,7 @@ fn spec_test_234() {
"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3307,7 +3307,7 @@ fn spec_test_235() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3323,7 +3323,7 @@ fn spec_test_236() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3339,7 +3339,7 @@ foo
"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3353,7 +3353,7 @@ fn spec_test_238() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3364,7 +3364,7 @@ fn spec_test_239() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3377,7 +3377,7 @@ fn spec_test_240() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3391,7 +3391,7 @@ fn spec_test_241() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3408,7 +3408,7 @@ fn spec_test_242() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3422,7 +3422,7 @@ bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3437,7 +3437,7 @@ fn spec_test_244() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3451,7 +3451,7 @@ fn spec_test_245() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3469,7 +3469,7 @@ fn spec_test_246() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3483,7 +3483,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3498,7 +3498,7 @@ baz

baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3513,7 +3513,7 @@ baz

baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3531,7 +3531,7 @@ bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3551,7 +3551,7 @@ baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3569,7 +3569,7 @@ fn spec_test_252() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3590,7 +3590,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3615,7 +3615,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3630,7 +3630,7 @@ fn spec_test_255() {

two

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3647,7 +3647,7 @@ fn spec_test_256() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3663,7 +3663,7 @@ fn spec_test_257() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3680,7 +3680,7 @@ fn spec_test_258() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3701,7 +3701,7 @@ fn spec_test_259() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3720,7 +3720,7 @@ fn spec_test_260() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3733,7 +3733,7 @@ fn spec_test_261() {

2.two

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3751,7 +3751,7 @@ fn spec_test_262() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3779,7 +3779,7 @@ fn spec_test_263() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3803,7 +3803,7 @@ baz "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3815,7 +3815,7 @@ fn spec_test_265() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3825,7 +3825,7 @@ fn spec_test_266() { let expected = r##"

1234567890. not ok

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3837,7 +3837,7 @@ fn spec_test_267() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3849,7 +3849,7 @@ fn spec_test_268() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3859,7 +3859,7 @@ fn spec_test_269() { let expected = r##"

-1. not ok

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3877,7 +3877,7 @@ fn spec_test_270() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3895,7 +3895,7 @@ fn spec_test_271() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3913,7 +3913,7 @@ paragraph "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3935,7 +3935,7 @@ fn spec_test_273() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3957,7 +3957,7 @@ fn spec_test_274() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3970,7 +3970,7 @@ bar

bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -3985,7 +3985,7 @@ fn spec_test_276() {

bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4002,7 +4002,7 @@ fn spec_test_277() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4029,7 +4029,7 @@ fn spec_test_278() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4042,7 +4042,7 @@ fn spec_test_279() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4057,7 +4057,7 @@ fn spec_test_280() {

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4073,7 +4073,7 @@ fn spec_test_281() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4089,7 +4089,7 @@ fn spec_test_282() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4105,7 +4105,7 @@ fn spec_test_283() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4117,7 +4117,7 @@ fn spec_test_284() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4134,7 +4134,7 @@ foo 1.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4159,7 +4159,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4184,7 +4184,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4209,7 +4209,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4230,7 +4230,7 @@ fn spec_test_289() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4255,7 +4255,7 @@ with two lines.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4269,7 +4269,7 @@ with two lines. "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4289,7 +4289,7 @@ continued here.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4309,7 +4309,7 @@ continued here.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4336,7 +4336,7 @@ fn spec_test_294() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4354,7 +4354,7 @@ fn spec_test_295() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4371,7 +4371,7 @@ fn spec_test_296() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4387,7 +4387,7 @@ fn spec_test_297() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4403,7 +4403,7 @@ fn spec_test_298() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4423,7 +4423,7 @@ fn spec_test_299() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4443,7 +4443,7 @@ baz "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4461,7 +4461,7 @@ fn spec_test_301() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4479,7 +4479,7 @@ fn spec_test_302() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4495,7 +4495,7 @@ fn spec_test_303() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4507,7 +4507,7 @@ fn spec_test_304() { 14. The number of doors is 6.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4521,7 +4521,7 @@ fn spec_test_305() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4546,7 +4546,7 @@ fn spec_test_306() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4574,7 +4574,7 @@ fn spec_test_307() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4598,7 +4598,7 @@ fn spec_test_308() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4627,7 +4627,7 @@ fn spec_test_309() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4651,7 +4651,7 @@ fn spec_test_310() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4675,7 +4675,7 @@ fn spec_test_311() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4695,7 +4695,7 @@ fn spec_test_312() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4718,7 +4718,7 @@ fn spec_test_313() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4741,7 +4741,7 @@ fn spec_test_314() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4762,7 +4762,7 @@ fn spec_test_315() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4787,7 +4787,7 @@ fn spec_test_316() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4811,7 +4811,7 @@ fn spec_test_317() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4836,7 +4836,7 @@ fn spec_test_318() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4860,7 +4860,7 @@ fn spec_test_319() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4880,7 +4880,7 @@ fn spec_test_320() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4904,7 +4904,7 @@ fn spec_test_321() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4916,7 +4916,7 @@ fn spec_test_322() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4933,7 +4933,7 @@ fn spec_test_323() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4953,7 +4953,7 @@ fn spec_test_324() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -4974,7 +4974,7 @@ fn spec_test_325() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5005,7 +5005,7 @@ fn spec_test_326() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5015,7 +5015,7 @@ fn spec_test_327() { let expected = r##"

hilo`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5025,7 +5025,7 @@ fn spec_test_328() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5035,7 +5035,7 @@ fn spec_test_329() { let expected = r##"

foo ` bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5045,7 +5045,7 @@ fn spec_test_330() { let expected = r##"

``

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5055,7 +5055,7 @@ fn spec_test_331() { let expected = r##"

``

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5065,7 +5065,7 @@ fn spec_test_332() { let expected = r##"

a

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5075,7 +5075,7 @@ fn spec_test_333() { let expected = r##"

 b 

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5087,7 +5087,7 @@ fn spec_test_334() {

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5101,7 +5101,7 @@ baz let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5113,7 +5113,7 @@ foo let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5124,7 +5124,7 @@ baz` let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5134,7 +5134,7 @@ fn spec_test_338() { let expected = r##"

foo\bar`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5144,7 +5144,7 @@ fn spec_test_339() { let expected = r##"

foo`bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5154,7 +5154,7 @@ fn spec_test_340() { let expected = r##"

foo `` bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5164,7 +5164,7 @@ fn spec_test_341() { let expected = r##"

*foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5174,7 +5174,7 @@ fn spec_test_342() { let expected = r##"

[not a link](/foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5184,7 +5184,7 @@ fn spec_test_343() { let expected = r##"

<a href="">`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5194,7 +5194,7 @@ fn spec_test_344() { let expected = r##"

`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5204,7 +5204,7 @@ fn spec_test_345() { let expected = r##"

<https://foo.bar.baz>`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5214,7 +5214,7 @@ fn spec_test_346() { let expected = r##"

https://foo.bar.`baz`

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5224,7 +5224,7 @@ fn spec_test_347() { let expected = r##"

```foo``

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5234,7 +5234,7 @@ fn spec_test_348() { let expected = r##"

`foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5244,7 +5244,7 @@ fn spec_test_349() { let expected = r##"

`foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5254,7 +5254,7 @@ fn spec_test_350() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5264,7 +5264,7 @@ fn spec_test_351() { let expected = r##"

a * foo bar*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5274,7 +5274,7 @@ fn spec_test_352() { let expected = r##"

a*"foo"*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5284,7 +5284,7 @@ fn spec_test_353() { let expected = r##"

* a *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5300,7 +5300,7 @@ fn spec_test_354() {

*€*charlie.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5310,7 +5310,7 @@ fn spec_test_355() { let expected = r##"

foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5320,7 +5320,7 @@ fn spec_test_356() { let expected = r##"

5678

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5330,7 +5330,7 @@ fn spec_test_357() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5340,7 +5340,7 @@ fn spec_test_358() { let expected = r##"

_ foo bar_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5350,7 +5350,7 @@ fn spec_test_359() { let expected = r##"

a_"foo"_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5360,7 +5360,7 @@ fn spec_test_360() { let expected = r##"

foo_bar_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5370,7 +5370,7 @@ fn spec_test_361() { let expected = r##"

5_6_78

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5380,7 +5380,7 @@ fn spec_test_362() { let expected = r##"

пристаням_стремятся_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5390,7 +5390,7 @@ fn spec_test_363() { let expected = r##"

aa_"bb"_cc

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5400,7 +5400,7 @@ fn spec_test_364() { let expected = r##"

foo-(bar)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5410,7 +5410,7 @@ fn spec_test_365() { let expected = r##"

_foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5420,7 +5420,7 @@ fn spec_test_366() { let expected = r##"

*foo bar *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5432,7 +5432,7 @@ fn spec_test_367() { *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5442,7 +5442,7 @@ fn spec_test_368() { let expected = r##"

*(*foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5452,7 +5452,7 @@ fn spec_test_369() { let expected = r##"

(foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5462,7 +5462,7 @@ fn spec_test_370() { let expected = r##"

foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5472,7 +5472,7 @@ fn spec_test_371() { let expected = r##"

_foo bar _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5482,7 +5482,7 @@ fn spec_test_372() { let expected = r##"

_(_foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5492,7 +5492,7 @@ fn spec_test_373() { let expected = r##"

(foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5502,7 +5502,7 @@ fn spec_test_374() { let expected = r##"

_foo_bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5512,7 +5512,7 @@ fn spec_test_375() { let expected = r##"

_пристаням_стремятся

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5522,7 +5522,7 @@ fn spec_test_376() { let expected = r##"

foo_bar_baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5532,7 +5532,7 @@ fn spec_test_377() { let expected = r##"

(bar).

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5542,7 +5542,7 @@ fn spec_test_378() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5552,7 +5552,7 @@ fn spec_test_379() { let expected = r##"

** foo bar**

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5562,7 +5562,7 @@ fn spec_test_380() { let expected = r##"

a**"foo"**

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5572,7 +5572,7 @@ fn spec_test_381() { let expected = r##"

foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5582,7 +5582,7 @@ fn spec_test_382() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5592,7 +5592,7 @@ fn spec_test_383() { let expected = r##"

__ foo bar__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5604,7 +5604,7 @@ foo bar__ foo bar__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5614,7 +5614,7 @@ fn spec_test_385() { let expected = r##"

a__"foo"__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5624,7 +5624,7 @@ fn spec_test_386() { let expected = r##"

foo__bar__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5634,7 +5634,7 @@ fn spec_test_387() { let expected = r##"

5__6__78

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5644,7 +5644,7 @@ fn spec_test_388() { let expected = r##"

пристаням__стремятся__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5654,7 +5654,7 @@ fn spec_test_389() { let expected = r##"

foo, bar, baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5664,7 +5664,7 @@ fn spec_test_390() { let expected = r##"

foo-(bar)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5674,7 +5674,7 @@ fn spec_test_391() { let expected = r##"

**foo bar **

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5684,7 +5684,7 @@ fn spec_test_392() { let expected = r##"

**(**foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5694,7 +5694,7 @@ fn spec_test_393() { let expected = r##"

(foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5706,7 +5706,7 @@ fn spec_test_394() { Asclepias physocarpa)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5716,7 +5716,7 @@ fn spec_test_395() { let expected = r##"

foo "bar" foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5726,7 +5726,7 @@ fn spec_test_396() { let expected = r##"

foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5736,7 +5736,7 @@ fn spec_test_397() { let expected = r##"

__foo bar __

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5746,7 +5746,7 @@ fn spec_test_398() { let expected = r##"

__(__foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5756,7 +5756,7 @@ fn spec_test_399() { let expected = r##"

(foo)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5766,7 +5766,7 @@ fn spec_test_400() { let expected = r##"

__foo__bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5776,7 +5776,7 @@ fn spec_test_401() { let expected = r##"

__пристаням__стремятся

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5786,7 +5786,7 @@ fn spec_test_402() { let expected = r##"

foo__bar__baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5796,7 +5796,7 @@ fn spec_test_403() { let expected = r##"

(bar).

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5806,7 +5806,7 @@ fn spec_test_404() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5818,7 +5818,7 @@ bar* bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5828,7 +5828,7 @@ fn spec_test_406() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5838,7 +5838,7 @@ fn spec_test_407() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5848,7 +5848,7 @@ fn spec_test_408() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5858,7 +5858,7 @@ fn spec_test_409() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5868,7 +5868,7 @@ fn spec_test_410() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5878,7 +5878,7 @@ fn spec_test_411() { let expected = r##"

foobarbaz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5888,7 +5888,7 @@ fn spec_test_412() { let expected = r##"

foo**bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5898,7 +5898,7 @@ fn spec_test_413() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5908,7 +5908,7 @@ fn spec_test_414() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5918,7 +5918,7 @@ fn spec_test_415() { let expected = r##"

foobar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5928,7 +5928,7 @@ fn spec_test_416() { let expected = r##"

foobarbaz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5938,7 +5938,7 @@ fn spec_test_417() { let expected = r##"

foobar***baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5948,7 +5948,7 @@ fn spec_test_418() { let expected = r##"

foo bar baz bim bop

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5958,7 +5958,7 @@ fn spec_test_419() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5968,7 +5968,7 @@ fn spec_test_420() { let expected = r##"

** is not an empty emphasis

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5978,7 +5978,7 @@ fn spec_test_421() { let expected = r##"

**** is not an empty strong emphasis

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -5988,7 +5988,7 @@ fn spec_test_422() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6000,7 +6000,7 @@ bar** bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6010,7 +6010,7 @@ fn spec_test_424() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6020,7 +6020,7 @@ fn spec_test_425() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6030,7 +6030,7 @@ fn spec_test_426() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6040,7 +6040,7 @@ fn spec_test_427() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6050,7 +6050,7 @@ fn spec_test_428() { let expected = r##"

foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6060,7 +6060,7 @@ fn spec_test_429() { let expected = r##"

foobarbaz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6070,7 +6070,7 @@ fn spec_test_430() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6080,7 +6080,7 @@ fn spec_test_431() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6092,7 +6092,7 @@ bim* bop** bim bop

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6102,7 +6102,7 @@ fn spec_test_433() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6112,7 +6112,7 @@ fn spec_test_434() { let expected = r##"

__ is not an empty emphasis

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6122,7 +6122,7 @@ fn spec_test_435() { let expected = r##"

____ is not an empty strong emphasis

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6132,7 +6132,7 @@ fn spec_test_436() { let expected = r##"

foo ***

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6142,7 +6142,7 @@ fn spec_test_437() { let expected = r##"

foo *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6152,7 +6152,7 @@ fn spec_test_438() { let expected = r##"

foo _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6162,7 +6162,7 @@ fn spec_test_439() { let expected = r##"

foo *****

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6172,7 +6172,7 @@ fn spec_test_440() { let expected = r##"

foo *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6182,7 +6182,7 @@ fn spec_test_441() { let expected = r##"

foo _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6192,7 +6192,7 @@ fn spec_test_442() { let expected = r##"

*foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6202,7 +6202,7 @@ fn spec_test_443() { let expected = r##"

foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6212,7 +6212,7 @@ fn spec_test_444() { let expected = r##"

*foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6222,7 +6222,7 @@ fn spec_test_445() { let expected = r##"

***foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6232,7 +6232,7 @@ fn spec_test_446() { let expected = r##"

foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6242,7 +6242,7 @@ fn spec_test_447() { let expected = r##"

foo***

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6252,7 +6252,7 @@ fn spec_test_448() { let expected = r##"

foo ___

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6262,7 +6262,7 @@ fn spec_test_449() { let expected = r##"

foo _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6272,7 +6272,7 @@ fn spec_test_450() { let expected = r##"

foo *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6282,7 +6282,7 @@ fn spec_test_451() { let expected = r##"

foo _____

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6292,7 +6292,7 @@ fn spec_test_452() { let expected = r##"

foo _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6302,7 +6302,7 @@ fn spec_test_453() { let expected = r##"

foo *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6312,7 +6312,7 @@ fn spec_test_454() { let expected = r##"

_foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6322,7 +6322,7 @@ fn spec_test_455() { let expected = r##"

foo_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6332,7 +6332,7 @@ fn spec_test_456() { let expected = r##"

_foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6342,7 +6342,7 @@ fn spec_test_457() { let expected = r##"

___foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6352,7 +6352,7 @@ fn spec_test_458() { let expected = r##"

foo_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6362,7 +6362,7 @@ fn spec_test_459() { let expected = r##"

foo___

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6372,7 +6372,7 @@ fn spec_test_460() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6382,7 +6382,7 @@ fn spec_test_461() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6392,7 +6392,7 @@ fn spec_test_462() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6402,7 +6402,7 @@ fn spec_test_463() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6412,7 +6412,7 @@ fn spec_test_464() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6422,7 +6422,7 @@ fn spec_test_465() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6432,7 +6432,7 @@ fn spec_test_466() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6442,7 +6442,7 @@ fn spec_test_467() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6452,7 +6452,7 @@ fn spec_test_468() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6462,7 +6462,7 @@ fn spec_test_469() { let expected = r##"

foo _bar baz_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6472,7 +6472,7 @@ fn spec_test_470() { let expected = r##"

foo bar *baz bim bam

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6482,7 +6482,7 @@ fn spec_test_471() { let expected = r##"

**foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6492,7 +6492,7 @@ fn spec_test_472() { let expected = r##"

*foo bar baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6502,7 +6502,7 @@ fn spec_test_473() { let expected = r##"

*bar*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6512,7 +6512,7 @@ fn spec_test_474() { let expected = r##"

_foo bar_

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6522,7 +6522,7 @@ fn spec_test_475() { let expected = r##"

*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6532,7 +6532,7 @@ fn spec_test_476() { let expected = r##"

**

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6542,7 +6542,7 @@ fn spec_test_477() { let expected = r##"

__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6552,7 +6552,7 @@ fn spec_test_478() { let expected = r##"

a *

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6562,7 +6562,7 @@ fn spec_test_479() { let expected = r##"

a _

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6572,7 +6572,7 @@ fn spec_test_480() { let expected = r##"

**ahttps://foo.bar/?q=**

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6582,7 +6582,7 @@ fn spec_test_481() { let expected = r##"

__ahttps://foo.bar/?q=__

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6592,7 +6592,7 @@ fn spec_test_482() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6602,7 +6602,7 @@ fn spec_test_483() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6612,7 +6612,7 @@ fn spec_test_484() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6622,7 +6622,7 @@ fn spec_test_485() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6632,7 +6632,7 @@ fn spec_test_486() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6642,7 +6642,7 @@ fn spec_test_487() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6652,7 +6652,7 @@ fn spec_test_488() { let expected = r##"

[link](/my uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6662,7 +6662,7 @@ fn spec_test_489() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6674,7 +6674,7 @@ bar) bar)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6686,7 +6686,7 @@ bar>) bar>)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6696,7 +6696,7 @@ fn spec_test_492() { let expected = r##"

a

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6706,7 +6706,7 @@ fn spec_test_493() { let expected = r##"

[link](<foo>)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6720,7 +6720,7 @@ fn spec_test_494() { [a](c)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6730,7 +6730,7 @@ fn spec_test_495() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6740,7 +6740,7 @@ fn spec_test_496() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6750,7 +6750,7 @@ fn spec_test_497() { let expected = r##"

[link](foo(and(bar))

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6760,7 +6760,7 @@ fn spec_test_498() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6770,7 +6770,7 @@ fn spec_test_499() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6780,7 +6780,7 @@ fn spec_test_500() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6796,7 +6796,7 @@ fn spec_test_501() {

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6806,7 +6806,7 @@ fn spec_test_502() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6816,7 +6816,7 @@ fn spec_test_503() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6826,7 +6826,7 @@ fn spec_test_504() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6840,7 +6840,7 @@ fn spec_test_505() { link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6850,7 +6850,7 @@ fn spec_test_506() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6860,7 +6860,7 @@ fn spec_test_507() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6870,7 +6870,7 @@ fn spec_test_508() { let expected = r##"

[link](/url "title "and" title")

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6880,7 +6880,7 @@ fn spec_test_509() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6891,7 +6891,7 @@ fn spec_test_510() { let expected = r##"

link

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6901,7 +6901,7 @@ fn spec_test_511() { let expected = r##"

[link] (/uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6911,7 +6911,7 @@ fn spec_test_512() { let expected = r##"

link [foo [bar]]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6921,7 +6921,7 @@ fn spec_test_513() { let expected = r##"

[link] bar](/uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6931,7 +6931,7 @@ fn spec_test_514() { let expected = r##"

[link bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6941,7 +6941,7 @@ fn spec_test_515() { let expected = r##"

link [bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6951,7 +6951,7 @@ fn spec_test_516() { let expected = r##"

link foo bar #

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6961,7 +6961,7 @@ fn spec_test_517() { let expected = r##"

moon

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6971,7 +6971,7 @@ fn spec_test_518() { let expected = r##"

[foo bar](/uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6981,7 +6981,7 @@ fn spec_test_519() { let expected = r##"

[foo [bar baz](/uri)](/uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -6991,7 +6991,7 @@ fn spec_test_520() { let expected = r##"

[foo](uri2)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7001,7 +7001,7 @@ fn spec_test_521() { let expected = r##"

*foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7011,7 +7011,7 @@ fn spec_test_522() { let expected = r##"

foo *bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7021,7 +7021,7 @@ fn spec_test_523() { let expected = r##"

foo [bar baz]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7031,7 +7031,7 @@ fn spec_test_524() { let expected = r##"

[foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7041,7 +7041,7 @@ fn spec_test_525() { let expected = r##"

[foo](/uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7051,7 +7051,7 @@ fn spec_test_526() { let expected = r##"

[foohttps://example.com/?search=](uri)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7063,7 +7063,7 @@ fn spec_test_527() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7075,7 +7075,7 @@ fn spec_test_528() { let expected = r##"

link [foo [bar]]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7087,7 +7087,7 @@ fn spec_test_529() { let expected = r##"

link [bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7099,7 +7099,7 @@ fn spec_test_530() { let expected = r##"

link foo bar #

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7111,7 +7111,7 @@ fn spec_test_531() { let expected = r##"

moon

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7123,7 +7123,7 @@ fn spec_test_532() { let expected = r##"

[foo bar]ref

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7135,7 +7135,7 @@ fn spec_test_533() { let expected = r##"

[foo bar baz]ref

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7147,7 +7147,7 @@ fn spec_test_534() { let expected = r##"

*foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7159,7 +7159,7 @@ fn spec_test_535() { let expected = r##"

foo *bar*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7171,7 +7171,7 @@ fn spec_test_536() { let expected = r##"

[foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7183,7 +7183,7 @@ fn spec_test_537() { let expected = r##"

[foo][ref]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7195,7 +7195,7 @@ fn spec_test_538() { let expected = r##"

[foohttps://example.com/?search=][ref]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7207,7 +7207,7 @@ fn spec_test_539() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7219,7 +7219,7 @@ fn spec_test_540() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7232,7 +7232,7 @@ fn spec_test_541() { let expected = r##"

Baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7244,7 +7244,7 @@ fn spec_test_542() { let expected = r##"

[foo] bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7258,7 +7258,7 @@ fn spec_test_543() { bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7272,7 +7272,7 @@ fn spec_test_544() { let expected = r##"

bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7284,7 +7284,7 @@ fn spec_test_545() { let expected = r##"

[bar][foo!]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7297,7 +7297,7 @@ fn spec_test_546() {

[ref[]: /uri

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7310,7 +7310,7 @@ fn spec_test_547() {

[ref[bar]]: /uri

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7323,7 +7323,7 @@ fn spec_test_548() {

[[[foo]]]: /url

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7335,7 +7335,7 @@ fn spec_test_549() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7347,7 +7347,7 @@ fn spec_test_550() { let expected = r##"

bar\

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7360,7 +7360,7 @@ fn spec_test_551() {

[]: /uri

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7377,7 +7377,7 @@ fn spec_test_552() { ]: /uri

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7389,7 +7389,7 @@ fn spec_test_553() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7401,7 +7401,7 @@ fn spec_test_554() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7413,7 +7413,7 @@ fn spec_test_555() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7427,7 +7427,7 @@ fn spec_test_556() { []

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7439,7 +7439,7 @@ fn spec_test_557() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7451,7 +7451,7 @@ fn spec_test_558() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7463,7 +7463,7 @@ fn spec_test_559() { let expected = r##"

[foo bar]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7475,7 +7475,7 @@ fn spec_test_560() { let expected = r##"

[[bar foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7487,7 +7487,7 @@ fn spec_test_561() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7499,7 +7499,7 @@ fn spec_test_562() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7511,7 +7511,7 @@ fn spec_test_563() { let expected = r##"

[foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7523,7 +7523,7 @@ fn spec_test_564() { let expected = r##"

*foo*

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7536,7 +7536,7 @@ fn spec_test_565() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7548,7 +7548,7 @@ fn spec_test_566() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7560,7 +7560,7 @@ fn spec_test_567() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7572,7 +7572,7 @@ fn spec_test_568() { let expected = r##"

foo(not a link)

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7584,7 +7584,7 @@ fn spec_test_569() { let expected = r##"

[foo]bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7597,7 +7597,7 @@ fn spec_test_570() { let expected = r##"

foobaz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7610,7 +7610,7 @@ fn spec_test_571() { let expected = r##"

[foo]bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7620,7 +7620,7 @@ fn spec_test_572() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7632,7 +7632,7 @@ fn spec_test_573() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7642,7 +7642,7 @@ fn spec_test_574() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7652,7 +7652,7 @@ fn spec_test_575() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7664,7 +7664,7 @@ fn spec_test_576() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7676,7 +7676,7 @@ fn spec_test_577() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7686,7 +7686,7 @@ fn spec_test_578() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7696,7 +7696,7 @@ fn spec_test_579() { let expected = r##"

My foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7706,7 +7706,7 @@ fn spec_test_580() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7716,7 +7716,7 @@ fn spec_test_581() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7728,7 +7728,7 @@ fn spec_test_582() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7740,7 +7740,7 @@ fn spec_test_583() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7752,7 +7752,7 @@ fn spec_test_584() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7764,7 +7764,7 @@ fn spec_test_585() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7776,7 +7776,7 @@ fn spec_test_586() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7790,7 +7790,7 @@ fn spec_test_587() { []

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7802,7 +7802,7 @@ fn spec_test_588() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7814,7 +7814,7 @@ fn spec_test_589() { let expected = r##"

foo bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7827,7 +7827,7 @@ fn spec_test_590() {

[[foo]]: /url "title"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7839,7 +7839,7 @@ fn spec_test_591() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7851,7 +7851,7 @@ fn spec_test_592() { let expected = r##"

![foo]

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7863,7 +7863,7 @@ fn spec_test_593() { let expected = r##"

!foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7873,7 +7873,7 @@ fn spec_test_594() { let expected = r##"

http://foo.bar.baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7883,7 +7883,7 @@ fn spec_test_595() { let expected = r##"

https://foo.bar.baz/test?q=hello&id=22&boolean

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7893,7 +7893,7 @@ fn spec_test_596() { let expected = r##"

irc://foo.bar:2233/baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7903,7 +7903,7 @@ fn spec_test_597() { let expected = r##"

MAILTO:FOO@BAR.BAZ

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7913,7 +7913,7 @@ fn spec_test_598() { let expected = r##"

a+b+c:d

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7923,7 +7923,7 @@ fn spec_test_599() { let expected = r##"

made-up-scheme://foo,bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7933,7 +7933,7 @@ fn spec_test_600() { let expected = r##"

https://../

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7943,7 +7943,7 @@ fn spec_test_601() { let expected = r##"

localhost:5001/foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7953,7 +7953,7 @@ fn spec_test_602() { let expected = r##"

<https://foo.bar/baz bim>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7963,7 +7963,7 @@ fn spec_test_603() { let expected = r##"

https://example.com/\[\

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7973,7 +7973,7 @@ fn spec_test_604() { let expected = r##"

foo@bar.example.com

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7983,7 +7983,7 @@ fn spec_test_605() { let expected = r##"

foo+special@Bar.baz-bar0.com

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -7993,7 +7993,7 @@ fn spec_test_606() { let expected = r##"

<foo+@bar.example.com>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8003,7 +8003,7 @@ fn spec_test_607() { let expected = r##"

<>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8013,7 +8013,7 @@ fn spec_test_608() { let expected = r##"

< https://foo.bar >

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8023,7 +8023,7 @@ fn spec_test_609() { let expected = r##"

<m:abc>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8033,7 +8033,7 @@ fn spec_test_610() { let expected = r##"

<foo.bar.baz>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8043,7 +8043,7 @@ fn spec_test_611() { let expected = r##"

https://example.com

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8053,7 +8053,7 @@ fn spec_test_612() { let expected = r##"

foo@bar.example.com

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8063,7 +8063,7 @@ fn spec_test_613() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8073,7 +8073,7 @@ fn spec_test_614() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8085,7 +8085,7 @@ data="foo" > data="foo" >

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8097,7 +8097,7 @@ _boolean zoop:33=zoop:33 /> _boolean zoop:33=zoop:33 />

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8107,7 +8107,7 @@ fn spec_test_617() { let expected = r##"

Foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8117,7 +8117,7 @@ fn spec_test_618() { let expected = r##"

<33> <__>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8127,7 +8127,7 @@ fn spec_test_619() { let expected = r##"

<a h*#ref="hi">

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8137,7 +8137,7 @@ fn spec_test_620() { let expected = r##"

<a href="hi'> <a href=hi'>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8153,7 +8153,7 @@ foo><bar/ > bim!bop />

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8163,7 +8163,7 @@ fn spec_test_622() { let expected = r##"

<a href='bar'title=title>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8173,7 +8173,7 @@ fn spec_test_623() { let expected = r##"

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8183,7 +8183,7 @@ fn spec_test_624() { let expected = r##"

</a href="foo">

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8195,7 +8195,7 @@ comment - with hyphens --> comment - with hyphens -->

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8208,7 +8208,7 @@ foo foo -->

foo foo -->

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8218,7 +8218,7 @@ fn spec_test_627() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8228,7 +8228,7 @@ fn spec_test_628() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8238,7 +8238,7 @@ fn spec_test_629() { let expected = r##"

foo &<]]>

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8248,7 +8248,7 @@ fn spec_test_630() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8258,7 +8258,7 @@ fn spec_test_631() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8268,7 +8268,7 @@ fn spec_test_632() { let expected = r##"

<a href=""">

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8280,7 +8280,7 @@ baz baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8292,7 +8292,7 @@ baz baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8304,7 +8304,7 @@ baz baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8316,7 +8316,7 @@ fn spec_test_636() { bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8328,7 +8328,7 @@ fn spec_test_637() { bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8340,7 +8340,7 @@ bar* bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8352,7 +8352,7 @@ bar* bar

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8363,7 +8363,7 @@ span` let expected = r##"

code span

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8374,7 +8374,7 @@ span` let expected = r##"

code\ span

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8386,7 +8386,7 @@ bar"> bar">

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8398,7 +8398,7 @@ bar"> bar">

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8408,7 +8408,7 @@ fn spec_test_644() { let expected = r##"

foo\

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8418,7 +8418,7 @@ fn spec_test_645() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8428,7 +8428,7 @@ fn spec_test_646() { let expected = r##"

foo\

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8438,7 +8438,7 @@ fn spec_test_647() { let expected = r##"

foo

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8450,7 +8450,7 @@ baz baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8462,7 +8462,7 @@ fn spec_test_649() { baz

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8472,7 +8472,7 @@ fn spec_test_650() { let expected = r##"

hello $.;'there

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8482,7 +8482,7 @@ fn spec_test_651() { let expected = r##"

Foo χρῆν

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -8492,5 +8492,5 @@ fn spec_test_652() { let expected = r##"

Multiple spaces

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } diff --git a/pulldown-cmark/tests/suite/strikethrough.rs b/pulldown-cmark/tests/suite/strikethrough.rs index a8799a3b..4e1457c2 100644 --- a/pulldown-cmark/tests/suite/strikethrough.rs +++ b/pulldown-cmark/tests/suite/strikethrough.rs @@ -10,7 +10,7 @@ fn strikethrough_test_1() { let expected = r##"

This is stricken out

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -20,7 +20,7 @@ fn strikethrough_test_2() { let expected = r##"

This is ~~stricken

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -30,7 +30,7 @@ fn strikethrough_test_3() { let expected = r##"

Thisisstricken

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -40,7 +40,7 @@ fn strikethrough_test_4() { let expected = r##"

Thisisstricken

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -50,7 +50,7 @@ fn strikethrough_test_5() { let expected = r##"

Here I strike out an exclamation point!.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -60,7 +60,7 @@ fn strikethrough_test_6() { let expected = r##"

This is stricken out

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -70,7 +70,7 @@ fn strikethrough_test_7() { let expected = r##"

This is ~stricken

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -80,7 +80,7 @@ fn strikethrough_test_8() { let expected = r##"

This~is~nothing

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -90,7 +90,7 @@ fn strikethrough_test_9() { let expected = r##"

This~is~nothing

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -100,7 +100,7 @@ fn strikethrough_test_10() { let expected = r##"

Here I fail to strike out an exclamation point~!~.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -110,7 +110,7 @@ fn strikethrough_test_11() { let expected = r##"

Here I fail to strike out a tilde ~~~.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -120,7 +120,7 @@ fn strikethrough_test_12() { let expected = r##"

Here I fail to match up ~~tildes~.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -130,7 +130,7 @@ fn strikethrough_test_13() { let expected = r##"

Here I fail to match up ~tildes~~.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -140,7 +140,7 @@ fn strikethrough_test_14() { let expected = r##"

This ~is stricken.

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -150,15 +150,5 @@ fn strikethrough_test_15() { let expected = r##"

This ~~is stricken.

"##; - test_markdown_html(original, expected, false, false, false); -} - -#[test] -fn strikethrough_test_16() { - let original = r##"~This ~~is stricken~ but this is not~~ -"##; - let expected = r##"

This ~~is stricken but this is not~~

-"##; - - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } diff --git a/pulldown-cmark/tests/suite/super_sub.rs b/pulldown-cmark/tests/suite/super_sub.rs new file mode 100644 index 00000000..6edebe2a --- /dev/null +++ b/pulldown-cmark/tests/suite/super_sub.rs @@ -0,0 +1,153 @@ +// This file is auto-generated by the build script +// Please, do not modify it manually + +use super::test_markdown_html; + +#[test] +fn super_sub_test_1() { + let original = r##"^This is super^ ~This is sub~ +"##; + let expected = r##"

This is super This is sub

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_2() { + let original = r##"~This is stricken out~ +"##; + let expected = r##"

This is stricken out

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_3() { + let original = r##"~This is \~stricken~ +"##; + let expected = r##"

This is ~stricken

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_4() { + let original = r##"~This~is~nothing~ +"##; + let expected = r##"

Thisisnothing

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_5() { + let original = r##"~This ~~is not stricken.~ +"##; + let expected = r##"

This ~~is not stricken.

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_6() { + let original = r##"~~This ~is~~ stricken.~ +"##; + let expected = r##"

This ~is stricken.~

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_7() { + let original = r##"~This ~~is stricken~ but this is not~~ +"##; + let expected = r##"

This ~~is stricken but this is not~~

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_8() { + let original = r##"H~2~O + +y=x^2^a+xb+c +"##; + let expected = r##"

H2O

+

y=x2a+xb+c

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_9() { + let original = r##"~foo~~ + +^bar^^ +"##; + let expected = r##"

~foo~~

+

^bar^^

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_10() { + let original = r##"~foo^~^bar~ + +*foo_*_bar* +"##; + let expected = r##"

foo^^bar~

+

foo__bar*

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_11() { + let original = r##"H^+^ + OH^-^ +"##; + let expected = r##"

H+ + OH-

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_12() { + let original = r##"Ca^2+^ + CO~3~^2-^ +"##; + let expected = r##"

Ca2+ + CO32-

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_13() { + let original = r##"NH~4~^+^ +"##; + let expected = r##"

NH4+

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} + +#[test] +fn super_sub_test_14() { + let original = r##"^+^ not superscript +"##; + let expected = r##"

^+^ not superscript

+"##; + + test_markdown_html(original, expected, false, false, false, true, false, false, false); +} diff --git a/pulldown-cmark/tests/suite/table.rs b/pulldown-cmark/tests/suite/table.rs index 6cc378ad..952c3db5 100644 --- a/pulldown-cmark/tests/suite/table.rs +++ b/pulldown-cmark/tests/suite/table.rs @@ -11,7 +11,7 @@ fn table_test_1() { let expected = r##"

Test header

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -23,7 +23,7 @@ fn table_test_2() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -44,7 +44,7 @@ fn table_test_3() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -71,7 +71,7 @@ fn table_test_4() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -87,7 +87,7 @@ fn table_test_5() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -103,7 +103,7 @@ fn table_test_6() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -119,7 +119,7 @@ fn table_test_7() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -135,7 +135,7 @@ fn table_test_8() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -161,7 +161,7 @@ fn table_test_9() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -173,7 +173,7 @@ fn table_test_10() { |ぃ|い|

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -187,7 +187,7 @@ fn table_test_11() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -201,7 +201,7 @@ fn table_test_12() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -302,7 +302,7 @@ b

b

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -316,7 +316,7 @@ fn table_test_14() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -332,7 +332,7 @@ fn table_test_15() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -348,7 +348,7 @@ fn table_test_16() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -398,7 +398,7 @@ fn table_test_17() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -436,7 +436,7 @@ fn table_test_18() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -466,7 +466,7 @@ fn table_test_19() { | Not | Enough |

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -480,7 +480,7 @@ fn table_test_20() {

|

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -494,7 +494,7 @@ fn table_test_21() { | Table | Body |

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -516,7 +516,7 @@ fn table_test_22() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -542,7 +542,7 @@ fn table_test_23() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -564,7 +564,7 @@ A: Interrupting —?

"##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -578,7 +578,7 @@ fn table_test_25() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -592,7 +592,7 @@ fn table_test_26() { "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -610,7 +610,7 @@ moo | moo "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } #[test] @@ -628,5 +628,5 @@ moo | moo "##; - test_markdown_html(original, expected, false, false, false); + test_markdown_html(original, expected, false, false, false, false, false, false, false); } diff --git a/pulldown-cmark/tests/suite/wikilinks.rs b/pulldown-cmark/tests/suite/wikilinks.rs new file mode 100644 index 00000000..1dcbbc11 --- /dev/null +++ b/pulldown-cmark/tests/suite/wikilinks.rs @@ -0,0 +1,256 @@ +// This file is auto-generated by the build script +// Please, do not modify it manually + +use super::test_markdown_html; + +#[test] +fn wikilinks_test_1() { + let original = r##"This is a [[WikiLink]]. +"##; + let expected = r##"

This is a WikiLink.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_2() { + let original = r##"This is a [[Main/WikiLink]]. +"##; + let expected = r##"

This is a Main/WikiLink.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_3() { + let original = r##"This is [[Ambiguous]]. + +[Ambiguous]: https://example.com/ +"##; + let expected = r##"

This is Ambiguous.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_4() { + let original = r##"[[squid] calamari is considered a delicacy](https://en.wikipedia.org/wiki/Squid) + +[calamari [squid]](https://en.wikipedia.org/wiki/Squid) +"##; + let expected = r##"

+[squid] calamari is considered a delicacy +

+

+calamari [squid] +

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_5() { + let original = r##"This is [also [[Ambiguous]]](https://example.com/). +"##; + let expected = r##"

This is [also Ambiguous](https://example.com/).

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_6() { + let original = r##" + +[[https://example.org/]] +"##; + let expected = r##"

https://example.org/

+

https://example.org/

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_7() { + let original = r##"This is [[WikiLink|a pothole]]. +"##; + let expected = r##"

This is a pothole.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_8() { + let original = r##"This is a [[WikiLink/In/A/Directory|WikiLink]]. +"##; + let expected = r##"

This is a WikiLink.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_9() { + let original = r##"This is [[WikiLink|a **strong** pothole]]. +"##; + let expected = r##"

This is a strong pothole.

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_10() { + let original = r##"This is a cute dog, linked to the page "WikiLink" + +[[WikiLink|![dog](dog.png)]] +"##; + let expected = r##"

This is a cute dog, linked to the page "WikiLink"

+

+dog +

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_11() { + let original = r##"[[WikiLink|[[Fish]]]] +"##; + let expected = r##"

[[WikiLink|Fish]]

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_12() { + let original = r##"[[WikiLink|[cat](cat.html)]] +"##; + let expected = r##"

[[WikiLink|cat]]

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_13() { + let original = r##"This is a cute dog. + +![[dog.png]] +"##; + let expected = r##"

This is a cute dog.

+

+dog.png +

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_14() { + let original = r##"![[dog.png|a cute dog]] +"##; + let expected = r##"

a cute dog

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_15() { + let original = r##"]] [[]] [[|]] [[|Symbol]] [[ +"##; + let expected = r##"

]] [[]] [[|]] [[|Symbol]] [[

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_16() { + let original = r##"[[link|]] +"##; + let expected = r##"

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_17() { + let original = r##"[[[[link|display]]]] +"##; + let expected = r##"

[[display]]

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_18() { + let original = r##"[inline link]([[url]]) +"##; + let expected = r##"

inline link

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_19() { + let original = r##"[inline link]([[url)]] +"##; + let expected = r##"

inline link]]

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_20() { + let original = r##"`[[code]]` +"##; + let expected = r##"

[[code]]

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_21() { + let original = r##"emphasis **cross [[over** here]] +"##; + let expected = r##"

emphasis **cross over** here

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_22() { + let original = r##"[[first\|second]] +"##; + let expected = r##"

second

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +} + +#[test] +fn wikilinks_test_23() { + let original = r##"[[first!second]] +"##; + let expected = r##"

first&#33;second

+"##; + + test_markdown_html(original, expected, false, false, false, false, true, false, false); +}