pulldown-cmark-0.10.3/.cargo_vcs_info.json0000644000000001540000000000100140540ustar { "git": { "sha1": "1cfaf02e516d24fd8c93f39b422548610c541aae" }, "path_in_vcs": "pulldown-cmark" }pulldown-cmark-0.10.3/Cargo.lock0000644000000434660000000000100120440ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bincode" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ "serde", ] [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ "unicode-width", ] [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "pulldown-cmark" version = "0.10.3" dependencies = [ "bincode", "bitflags", "criterion", "getopts", "lazy_static", "memchr", "pulldown-cmark-escape", "regex", "serde", "serde_json", "unicase", ] [[package]] name = "pulldown-cmark-escape" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[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.198" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[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 = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" pulldown-cmark-0.10.3/Cargo.toml0000644000000044500000000000100120550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.74" name = "pulldown-cmark" version = "0.10.3" authors = [ "Raph Levien ", "Marcus Klaas de Vries ", ] build = "build.rs" exclude = [ "/third_party/**/*", "/tools/**/*", "/specs/**/*", "/azure-pipelines.yml", ] description = "A pull parser for CommonMark" readme = "README.md" keywords = [ "markdown", "commonmark", ] categories = ["text-processing"] license = "MIT" repository = "https://github.com/raphlinus/pulldown-cmark" [[bin]] name = "pulldown-cmark" doc = false required-features = ["getopts"] [[example]] name = "event-filter" required-features = ["html"] [[example]] name = "footnote-rewrite" required-features = ["html"] [[example]] name = "parser-map-event-print" required-features = ["html"] [[example]] name = "parser-map-tag-print" required-features = ["html"] [[example]] name = "string-to-string" required-features = ["html"] [[example]] name = "broken-link-callbacks" required-features = ["html"] [[bench]] name = "html_rendering" harness = false [[bench]] name = "lib" harness = false [[bench]] name = "markdown-it" harness = false [dependencies.bitflags] version = "2" [dependencies.getopts] version = "0.2" optional = true [dependencies.memchr] version = "2.5" [dependencies.pulldown-cmark-escape] version = "0.10.0" optional = true [dependencies.serde] version = "1.0" features = ["derive"] optional = true [dependencies.unicase] version = "2.6" [dev-dependencies.bincode] version = "1.3.1" [dev-dependencies.criterion] version = "0.5" [dev-dependencies.lazy_static] version = "1.4" [dev-dependencies.regex] version = "1.6" [dev-dependencies.serde_json] version = "1.0.61" [features] default = [ "getopts", "html", ] gen-tests = [] html = ["pulldown-cmark-escape"] simd = ["pulldown-cmark-escape?/simd"] pulldown-cmark-0.10.3/Cargo.toml.orig0000644000000033240000000000100130130ustar [package] name = "pulldown-cmark" version = "0.10.3" authors = [ "Raph Levien ", "Marcus Klaas de Vries ", ] license = "MIT" description = "A pull parser for CommonMark" repository = "https://github.com/raphlinus/pulldown-cmark" keywords = ["markdown", "commonmark"] categories = ["text-processing"] edition = "2021" rust-version = "1.74" # Update README.md and GitHub action when changing this readme = "../README.md" exclude = [ "/third_party/**/*", "/tools/**/*", "/specs/**/*", "/azure-pipelines.yml", ] build = "build.rs" [[bin]] name = "pulldown-cmark" required-features = ["getopts"] doc = false [[bench]] name = "html_rendering" harness = false [[bench]] name = "lib" harness = false [[bench]] name = "markdown-it" harness = false [[example]] name = "event-filter" required-features = ["html"] [[example]] name = "footnote-rewrite" required-features = ["html"] [[example]] name = "parser-map-event-print" required-features = ["html"] [[example]] name = "parser-map-tag-print" required-features = ["html"] [[example]] name = "string-to-string" required-features = ["html"] [[example]] name = "broken-link-callbacks" required-features = ["html"] [dependencies] bitflags = "2" unicase = "2.6" memchr = "2.5" getopts = { version = "0.2", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } pulldown-cmark-escape = { path = "../pulldown-cmark-escape", version = "0.10.0", optional = true } [dev-dependencies] lazy_static = "1.4" criterion = "0.5" regex = "1.6" serde_json = "1.0.61" bincode = "1.3.1" [features] default = ["getopts", "html"] gen-tests = [] simd = ["pulldown-cmark-escape?/simd"] html = ["pulldown-cmark-escape"] pulldown-cmark-0.10.3/Cargo.toml.orig000064400000000000000000000033241046102023000155350ustar 00000000000000[package] name = "pulldown-cmark" version = "0.10.3" authors = [ "Raph Levien ", "Marcus Klaas de Vries ", ] license = "MIT" description = "A pull parser for CommonMark" repository = "https://github.com/raphlinus/pulldown-cmark" keywords = ["markdown", "commonmark"] categories = ["text-processing"] edition = "2021" rust-version = "1.74" # Update README.md and GitHub action when changing this readme = "../README.md" exclude = [ "/third_party/**/*", "/tools/**/*", "/specs/**/*", "/azure-pipelines.yml", ] build = "build.rs" [[bin]] name = "pulldown-cmark" required-features = ["getopts"] doc = false [[bench]] name = "html_rendering" harness = false [[bench]] name = "lib" harness = false [[bench]] name = "markdown-it" harness = false [[example]] name = "event-filter" required-features = ["html"] [[example]] name = "footnote-rewrite" required-features = ["html"] [[example]] name = "parser-map-event-print" required-features = ["html"] [[example]] name = "parser-map-tag-print" required-features = ["html"] [[example]] name = "string-to-string" required-features = ["html"] [[example]] name = "broken-link-callbacks" required-features = ["html"] [dependencies] bitflags = "2" unicase = "2.6" memchr = "2.5" getopts = { version = "0.2", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } pulldown-cmark-escape = { path = "../pulldown-cmark-escape", version = "0.10.0", optional = true } [dev-dependencies] lazy_static = "1.4" criterion = "0.5" regex = "1.6" serde_json = "1.0.61" bincode = "1.3.1" [features] default = ["getopts", "html"] gen-tests = [] simd = ["pulldown-cmark-escape?/simd"] html = ["pulldown-cmark-escape"] pulldown-cmark-0.10.3/LICENSE000064400000000000000000000021011046102023000136430ustar 00000000000000The MIT License Copyright 2015 Google Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pulldown-cmark-0.10.3/README.md000064400000000000000000000161061046102023000141270ustar 00000000000000# pulldown-cmark [![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) [![Crates.io](https://img.shields.io/crates/v/pulldown-cmark.svg?maxAge=2592000)](https://crates.io/crates/pulldown-cmark) [Documentation](https://docs.rs/pulldown-cmark/) This library is a pull parser for [CommonMark](http://commonmark.org/), written in [Rust](http://www.rust-lang.org/). It comes with a simple command-line tool, useful for rendering to HTML, and is also designed to be easy to use from as 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/) 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-). Rustc 1.74 or newer is required to build the crate. ## Example Example usage: ```rust // Create parser with example Markdown text. let markdown_input = "hello world"; let parser = pulldown_cmark::Parser::new(markdown_input); // Write to a new String buffer. let mut html_output = String::new(); pulldown_cmark::html::push_html(&mut html_output, parser); assert_eq!(&html_output, "

hello world

\n"); ``` ## Why a pull parser? There are many parsers for Markdown and its variants, but to my knowledge none use pull parsing. Pull parsing has become popular for XML, especially for memory-conscious applications, because it uses dramatically less memory than constructing a document tree, but is much easier to use than push parsers. Push parsers are notoriously difficult to use, and also often error-prone because of the need for user to delicately juggle state in a series of callbacks. In a clean design, the parsing and rendering stages are neatly separated, but this is often sacrificed in the name of performance and expedience. Many Markdown implementations mix parsing and rendering together, and even designs that try to separate them (such as the popular [hoedown](https://github.com/hoedown/hoedown)), make the assumption that the rendering process can be fully represented as a serialized string. Pull parsing is in some sense the most versatile architecture. It's possible to drive a push interface, also with minimal memory, and quite straightforward to construct an AST. Another advantage is that source-map information (the mapping between parsed blocks and offsets within the source text) is readily available; you can call `into_offset_iter()` to create an iterator that yields `(Event, Range)` pairs, where the second element is the event's corresponding range in the source document. While manipulating ASTs is the most flexible way to transform documents, operating on iterators is surprisingly easy, and quite efficient. Here, for example, is the code to transform soft line breaks into hard breaks: ```rust let parser = parser.map(|event| match event { Event::SoftBreak => Event::HardBreak, _ => event }); ``` Or expanding an abbreviation in text: ```rust let parser = parser.map(|event| match event { Event::Text(text) => Event::Text(text.replace("abbr", "abbreviation").into()), _ => event }); ``` Another simple example is code to determine the max nesting level: ```rust let mut max_nesting = 0; let mut level = 0; for event in parser { match event { Event::Start(_) => { level += 1; max_nesting = std::cmp::max(max_nesting, level); } Event::End(_) => level -= 1, _ => () } } ``` Note that consecutive text events can happen due to the manner in which the parser evaluates the source. A utility `TextMergeStream` exists to improve the comfort of iterating the events: ```rust use pulldown_cmark::{Event, Parser, Options}; let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example."; let iterator = TextMergeStream::new(Parser::new(markdown_input)); for event in iterator { match event { Event::Text(text) => println!("{}", text), _ => {} } } ``` There are some basic but fully functional examples of the usage of the crate in the `examples` directory of this repository. ## Using Rust idiomatically A lot of the internal scanning code is written at a pretty low level (it pretty much scans byte patterns for the bits of syntax), but the external interface is designed to be idiomatic Rust. Pull parsers are at heart an iterator of events (start and end tags, text, and other bits and pieces). The parser data structure implements the Rust Iterator trait directly, and Event is an enum. Thus, you can use the full power and expressivity of Rust's iterator infrastructure, including for loops and `map` (as in the examples above), collecting the events into a vector (for recording, playback, and manipulation), and more. Further, the `Text` event (representing text) is a small copy-on-write string. The vast majority of text fragments are just slices of the source document. For these, copy-on-write gives a convenient representation that requires no allocation or copying, but allocated strings are available when they're needed. Thus, when rendering text to HTML, most text is copied just once, from the source document to the HTML buffer. When using the pulldown-cmark's own HTML renderer, make sure to write to a buffered target like a `Vec` or `String`. Since it performs many (very) small writes, writing directly to stdout, files, or sockets is detrimental to performance. Such writers can be wrapped in a [`BufWriter`](https://doc.rust-lang.org/std/io/struct.BufWriter.html). ## Build options By default, the binary is built as well. If you don't want/need it, then build like this: ```bash > cargo build --no-default-features ``` Or put in your `Cargo.toml` file: ```toml pulldown-cmark = { version = "0.10.3", default-features = false } ``` SIMD accelerated scanners are available for the x64 platform from version 0.5 onwards. To enable them, build with simd feature: ```bash > cargo build --release --features simd ``` Or add the feature to your project's `Cargo.toml`: ```toml pulldown-cmark = { version = "0.10.3", default-features = false, features = ["simd"] } ``` For a higher release performance you may want this configuration in your profile release: ``` lto = true codegen-units = 1 panic = "abort" ``` ## Authors The main author is Raph Levien. The implementation of the new design (v0.3+) was completed by Marcus Klaas de Vries. Since 2023, the development has been driven by Martín Pozo, Michael Howell, Roope Salmi and Martin Geisler. ## License This software is under the MIT license. See details in [license file](./LICENSE). ## Contributions We gladly accept contributions via GitHub pull requests. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for more details. pulldown-cmark-0.10.3/build.rs000064400000000000000000000157221046102023000143200ustar 00000000000000fn main() { generate_tests_from_spec() } // If the "gen-tests" feature is absent, // this function will be compiled down to nothing #[cfg(not(feature = "gen-tests"))] fn generate_tests_from_spec() {} // If the feature is present, generate tests // from any .txt file present in the specs/ directory // // Test cases are present in the files in the // following format: // // ```````````````````````````````` example // markdown // . // expected html output // ```````````````````````````````` #[cfg(feature = "gen-tests")] fn generate_tests_from_spec() { use std::fs::{self, File}; use std::io::{Read, Write}; use std::path::PathBuf; // This is a hardcoded path to the CommonMark spec because it is not situated in // the specs/ directory. It's in an array to easily chain it to the other iterator // and make it easy to eventually add other hardcoded paths in the future if needed let hardcoded = [ "./third_party/CommonMark/spec.txt", "./third_party/CommonMark/smart_punct.txt", "./third_party/GitHub/gfm_table.txt", "./third_party/GitHub/gfm_strikethrough.txt", "./third_party/GitHub/gfm_tasklist.txt", ]; let hardcoded_iter = hardcoded.iter().map(PathBuf::from); // Create an iterator over the files in the specs/ directory that have a .txt extension let mut spec_files = fs::read_dir("./specs") .expect("Could not find the 'specs' directory") .filter_map(Result::ok) .map(|d| d.path()) .filter(|p| p.extension().map(|e| e.to_owned()).is_some()) .chain(hardcoded_iter) .collect::>(); // Sort by spec names spec_files.sort_by(|p, q| p.file_stem().cmp(&q.file_stem())); let spec_files = spec_files; for file_path in &spec_files { let mut raw_spec = String::new(); File::open(&file_path) .and_then(|mut f| f.read_to_string(&mut raw_spec)) .expect("Could not read the spec file"); let rs_test_file = PathBuf::from("./tests/suite/") .join(file_path.file_name().expect("Invalid filename")) .with_extension("rs"); let mut spec_rs = File::create(&rs_test_file).expect(&format!("Could not create {:?}", rs_test_file)); let spec_name = file_path.file_stem().unwrap().to_str().unwrap(); let spec = Spec::new(&raw_spec); let mut n_tests = 0; spec_rs .write_all(b"// This file is auto-generated by the build script\n") .unwrap(); spec_rs .write_all(b"// Please, do not modify it manually\n") .unwrap(); spec_rs .write_all(b"\nuse super::test_markdown_html;\n") .unwrap(); for (i, testcase) in spec.enumerate() { spec_rs .write_fmt(format_args!( r###" #[test] fn {}_test_{i}() {{ let original = r##"{original}"##; let expected = r##"{expected}"##; test_markdown_html(original, expected, {smart_punct}, {metadata_blocks}, {old_footnotes}); }} "###, spec_name, i = i + 1, original = testcase.original, expected = testcase.expected, smart_punct = testcase.smart_punct, metadata_blocks = testcase.metadata_blocks, old_footnotes = testcase.old_footnotes, )) .unwrap(); n_tests += 1; } println!( "cargo:warning=Generated {} tests in {:?}", n_tests, rs_test_file ); } // write mods to suite/mod.rs let suite_mod_file = PathBuf::from("./tests/suite/mod").with_extension("rs"); let mut mod_rs = File::create(&suite_mod_file).expect(&format!("Could not create {:?}", &suite_mod_file)); mod_rs .write_all(b"// This file is auto-generated by the build script\n") .unwrap(); mod_rs .write_all(b"// Please, do not modify it manually\n") .unwrap(); mod_rs .write_all(b"\npub use super::test_markdown_html;\n\n") .unwrap(); for file_path in &spec_files { let mod_name = file_path.file_stem().unwrap().to_str().unwrap(); mod_rs.write_all(b"mod ").unwrap(); mod_rs.write_all(mod_name.as_bytes()).unwrap(); mod_rs.write_all(b";\n").unwrap(); } } #[cfg(feature = "gen-tests")] pub struct Spec<'a> { spec: &'a str, } #[cfg(feature = "gen-tests")] impl<'a> Spec<'a> { pub fn new(spec: &'a str) -> Self { Spec { spec } } } #[cfg(feature = "gen-tests")] pub struct TestCase { pub original: String, pub expected: String, pub smart_punct: bool, pub metadata_blocks: bool, pub old_footnotes: bool, } #[cfg(feature = "gen-tests")] impl<'a> Iterator for Spec<'a> { type Item = TestCase; fn next(&mut self) -> Option { 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_end = self.spec[i_start..] .find("\n.\n") .map(|pos| (pos + 1) + i_start)?; let e_end = self.spec[i_end + 2..] .find("````````````````````````````````\n") .map(|pos| pos + i_end + 2)?; self.spec = &self.spec[e_end + 33..]; let test_case = TestCase { original: spec[i_start..i_end].to_string().replace("→", "\t"), expected: spec[i_end + 2..e_end].to_string().replace("→", "\t"), smart_punct, metadata_blocks, old_footnotes, }; Some(test_case) } } pulldown-cmark-0.10.3/examples/broken-link-callbacks.rs000064400000000000000000000025411046102023000211620ustar 00000000000000use pulldown_cmark::{html, BrokenLink, Options, Parser}; fn main() { let input: &str = "Hello world, check out [my website][]."; println!("Parsing the following markdown string:\n{}", input); // Setup callback that sets the URL and title when it encounters // a reference to our home page. let callback = |broken_link: BrokenLink| { if broken_link.reference.as_ref() == "my website" { println!( "Replacing the markdown `{}` of type {:?} with a working link", &input[broken_link.span], broken_link.link_type, ); Some(("http://example.com".into(), "my example website".into())) } else { None } }; // Create a parser with our callback function for broken links. let parser = Parser::new_with_broken_link_callback(input, Options::empty(), Some(callback)); // Write to String buffer. let mut html_output: String = String::with_capacity(input.len() * 3 / 2); html::push_html(&mut html_output, parser); // Check that the output is what we expected. let expected_html: &str = "

Hello world, check out my website.

\n"; assert_eq!(expected_html, &html_output); // Write result to stdout. println!("\nHTML output:\n{}", &html_output); } pulldown-cmark-0.10.3/examples/event-filter.rs000064400000000000000000000020371046102023000174360ustar 00000000000000use std::io::Write as _; use pulldown_cmark::{html, Event, Options, Parser, Tag, TagEnd}; fn main() { let markdown_input: &str = "This is Peter on ![holiday in Greece](pearl_beach.jpg)."; println!("Parsing the following markdown string:\n{}", markdown_input); // Set up parser. We can treat is as any other iterator. We replace Peter by John // and image by its alt text. let parser = Parser::new_ext(markdown_input, Options::empty()) .map(|event| match event { Event::Text(text) => Event::Text(text.replace("Peter", "John").into()), _ => event, }) .filter(|event| match event { Event::Start(Tag::Image { .. }) | Event::End(TagEnd::Image) => false, _ => true, }); // 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(&mut handle, parser).unwrap(); } pulldown-cmark-0.10.3/examples/events.rs000064400000000000000000000010121046102023000163260ustar 00000000000000use std::io::Read; use pulldown_cmark::{Event, Parser}; /// Show all events from the text on stdin. fn main() { let mut text = String::new(); std::io::stdin().read_to_string(&mut text).unwrap(); eprintln!("{text:?} -> ["); let mut width = 0; for event in Parser::new(&text) { if let Event::End(_) = event { width -= 2; } eprintln!(" {:width$}{event:?}", ""); if let Event::Start(_) = event { width += 2; } } eprintln!("]"); } pulldown-cmark-0.10.3/examples/footnote-rewrite.rs000064400000000000000000000165511046102023000203540ustar 00000000000000use std::collections::HashMap; use std::fmt::Write as _; use std::io::Write as _; use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag, TagEnd}; /// This example shows how to do footnotes as bottom-notes, in the style of GitHub. fn main() { let markdown_input: &str = "This is an [^a] footnote [^a].\n\n[^a]: footnote contents"; println!("Parsing the following markdown string:\n{}", markdown_input); // To generate this style, you have to collect the footnotes at the end, while parsing. // You also need to count usages. let mut footnotes = Vec::new(); let mut in_footnote = Vec::new(); let mut footnote_numbers = HashMap::new(); // ENABLE_FOOTNOTES is used in this example, but ENABLE_OLD_FOOTNOTES would work, too. let parser = Parser::new_ext(markdown_input, Options::ENABLE_FOOTNOTES) .filter_map(|event| { match event { Event::Start(Tag::FootnoteDefinition(_)) => { in_footnote.push(vec![event]); None } Event::End(TagEnd::FootnoteDefinition) => { let mut f = in_footnote.pop().unwrap(); f.push(event); footnotes.push(f); None } Event::FootnoteReference(name) => { let n = footnote_numbers.len() + 1; let (n, nr) = footnote_numbers.entry(name.clone()).or_insert((n, 0usize)); *nr += 1; let html = Event::Html(format!(r##"[{n}]"##).into()); if in_footnote.is_empty() { Some(html) } else { in_footnote.last_mut().unwrap().push(html); None } } _ if !in_footnote.is_empty() => { in_footnote.last_mut().unwrap().push(event); None } _ => Some(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(&mut handle, parser).unwrap(); // To make the footnotes look right, we need to sort them by their appearance order, not by // the in-tree order of their actual definitions. Unused items are omitted entirely. // // For example, this code: // // test [^1] [^2] // [^2]: second used, first defined // [^1]: test // // Gets rendered like *this* if you copy it into a GitHub comment box: // //

test [1] [2]

//
//
    //
  1. test ↩
  2. //
  3. second used, first defined ↩
  4. //
if !footnotes.is_empty() { footnotes.retain(|f| match f.first() { Some(Event::Start(Tag::FootnoteDefinition(name))) => { footnote_numbers.get(name).unwrap_or(&(0, 0)).1 != 0 } _ => false, }); footnotes.sort_by_cached_key(|f| match f.first() { Some(Event::Start(Tag::FootnoteDefinition(name))) => { footnote_numbers.get(name).unwrap_or(&(0, 0)).0 } _ => unreachable!(), }); handle .write_all(b"
    \n") .unwrap(); html::write_html( &mut handle, footnotes.into_iter().flat_map(|fl| { // To write backrefs, the name needs kept until the end of the footnote definition. let mut name = CowStr::from(""); // Backrefs are included in the final paragraph of the footnote, if it's normal text. // For example, this DOM can be produced: // // Markdown: // // five [^feet]. // // [^feet]: // A foot is defined, in this case, as 0.3048 m. // // Historically, the foot has not been defined this way, corresponding to many // subtly different units depending on the location. // // HTML: // //

    five [1].

    // //
      //
    1. //

      A foot is defined, in this case, as 0.3048 m.

      //

      Historically, the foot has not been defined this way, corresponding to many // subtly different units depending on the location.

      //
    2. //
    // // This is mostly a visual hack, so that footnotes use less vertical space. // // If there is no final paragraph, such as a tabular, list, or image footnote, it gets // pushed after the last tag instead. let mut has_written_backrefs = false; let fl_len = fl.len(); let footnote_numbers = &footnote_numbers; fl.into_iter().enumerate().map(move |(i, f)| match f { Event::Start(Tag::FootnoteDefinition(current_name)) => { name = current_name; has_written_backrefs = false; Event::Html(format!(r##"
  1. "##).into()) } Event::End(TagEnd::FootnoteDefinition) | Event::End(TagEnd::Paragraph) if !has_written_backrefs && i >= fl_len - 2 => { let usage_count = footnote_numbers.get(&name).unwrap().1; let mut end = String::with_capacity( name.len() + (r##"
  2. "##.len() * usage_count), ); for usage in 1..=usage_count { if usage == 1 { write!(&mut end, r##" "##) .unwrap(); } else { write!(&mut end, r##" ↩{usage}"##) .unwrap(); } } has_written_backrefs = true; if f == Event::End(TagEnd::FootnoteDefinition) { end.push_str("\n"); } else { end.push_str("

    \n"); } Event::Html(end.into()) } Event::End(TagEnd::FootnoteDefinition) => Event::Html("\n".into()), Event::FootnoteReference(_) => unreachable!("converted to HTML earlier"), f => f, }) }), ) .unwrap(); handle.write_all(b"
\n").unwrap(); } } pulldown-cmark-0.10.3/examples/parser-map-event-print.rs000064400000000000000000000026761046102023000213630ustar 00000000000000use pulldown_cmark::{html, Event, Parser}; fn main() { let markdown_input = "# Example Heading\nExample paragraph with **lorem** _ipsum_ text."; println!( "\nParsing the following markdown string:\n{}\n", markdown_input ); // Set up the parser. We can treat is as any other iterator. // For each event, we print its details, such as the tag or string. // This filter simply returns the same event without any changes; // you can compare the `event-filter` example which alters the output. let parser = Parser::new(markdown_input).map(|event| { match &event { Event::Start(tag) => println!("Start: {:?}", tag), Event::End(tag) => println!("End: {:?}", tag), Event::Html(s) => println!("Html: {:?}", s), Event::InlineHtml(s) => println!("InlineHtml: {:?}", s), Event::Text(s) => println!("Text: {:?}", s), Event::Code(s) => println!("Code: {:?}", s), Event::FootnoteReference(s) => println!("FootnoteReference: {:?}", s), Event::TaskListMarker(b) => println!("TaskListMarker: {:?}", b), Event::SoftBreak => println!("SoftBreak"), Event::HardBreak => println!("HardBreak"), Event::Rule => println!("Rule"), }; event }); let mut html_output = String::new(); html::push_html(&mut html_output, parser); println!("\nHTML output:\n{}\n", &html_output); } pulldown-cmark-0.10.3/examples/parser-map-tag-print.rs000064400000000000000000000075411046102023000210110ustar 00000000000000use pulldown_cmark::{Event, Options, Parser, Tag}; fn main() { let markdown_input = concat!( "# My Heading\n", "\n", "My paragraph.\n", "\n", "* a\n", "* b\n", "* c\n", "\n", "1. d\n", "2. e\n", "3. f\n", "\n", "> my block quote\n", "\n", "```\n", "my code block\n", "```\n", "\n", "*emphasis*\n", "**strong**\n", "~~strikethrough~~\n", "[My Link](http://example.com)\n", "![My Image](http://example.com/image.jpg)\n", "\n", "| a | b |\n", "| - | - |\n", "| c | d |\n", "\n", "hello[^1]\n", "[^1]: my footnote\n", ); println!( "\nParsing the following markdown string:\n{}\n", markdown_input ); // Set up the parser. We can treat is as any other iterator. // For each event, we print its details, such as the tag or string. // This filter simply returns the same event without any changes; // you can compare the `event-filter` example which alters the output. let parser = Parser::new_ext(markdown_input, Options::all()).map(|event| { match &event { Event::Start(tag) => match tag { Tag::HtmlBlock => println!("HtmlBlock"), Tag::Heading { level, id, classes, attrs, } => println!( "Heading heading_level: {} fragment identifier: {:?} classes: {:?} attrs: {:?}", level, id, classes, attrs ), Tag::Paragraph => println!("Paragraph"), Tag::List(ordered_list_first_item_number) => println!( "List ordered_list_first_item_number: {:?}", ordered_list_first_item_number ), Tag::Item => println!("Item (this is a list item)"), Tag::Emphasis => println!("Emphasis (this is a span tag)"), Tag::Strong => println!("Strong (this is a span tag)"), Tag::Strikethrough => println!("Strikethrough (this is a span tag)"), Tag::BlockQuote => println!("BlockQuote"), Tag::CodeBlock(code_block_kind) => { println!("CodeBlock code_block_kind: {:?}", code_block_kind) } Tag::Link { link_type, dest_url, title, id, } => println!( "Link link_type: {:?} url: {} title: {} id: {}", link_type, dest_url, title, id ), Tag::Image { link_type, dest_url, title, id, } => println!( "Image link_type: {:?} url: {} title: {} id: {}", link_type, dest_url, title, id ), Tag::Table(column_text_alignment_list) => println!( "Table column_text_alignment_list: {:?}", column_text_alignment_list ), Tag::TableHead => println!("TableHead (contains TableRow tags"), Tag::TableRow => println!("TableRow (contains TableCell tags)"), Tag::TableCell => println!("TableCell (contains inline tags)"), Tag::FootnoteDefinition(label) => println!("FootnoteDefinition label: {}", label), Tag::MetadataBlock(kind) => println!("MetadataBlock: {:?}", kind), }, _ => (), }; event }); let mut html_output = String::new(); pulldown_cmark::html::push_html(&mut html_output, parser); println!("\nHTML output:\n{}\n", &html_output); } pulldown-cmark-0.10.3/examples/string-to-string.rs000064400000000000000000000017411046102023000202650ustar 00000000000000use pulldown_cmark::{html, Options, Parser}; fn main() { let markdown_input: &str = "Hello world, this is a ~~complicated~~ *very simple* example."; println!("Parsing the following markdown string:\n{}", markdown_input); // Set up options and parser. Strikethroughs are not part of the CommonMark standard // and we therefore must enable it explicitly. let mut options = Options::empty(); options.insert(Options::ENABLE_STRIKETHROUGH); let parser = Parser::new_ext(markdown_input, options); // Write to String buffer. let mut html_output: String = String::with_capacity(markdown_input.len() * 3 / 2); html::push_html(&mut html_output, parser); // Check that the output is what we expected. let expected_html: &str = "

Hello world, this is a complicated very simple example.

\n"; assert_eq!(expected_html, &html_output); // Write result to stdout. println!("\nHTML output:\n{}", &html_output); } pulldown-cmark-0.10.3/src/entities.rs000064400000000000000000001772411046102023000156410ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Expansions of HTML5 entities // Autogenerated by mk_entities.py const ENTITIES: [(&[u8], &str); 2125] = [ (b"AElig", "\u{00C6}"), (b"AMP", "\u{0026}"), (b"Aacute", "\u{00C1}"), (b"Abreve", "\u{0102}"), (b"Acirc", "\u{00C2}"), (b"Acy", "\u{0410}"), (b"Afr", "\u{1D504}"), (b"Agrave", "\u{00C0}"), (b"Alpha", "\u{0391}"), (b"Amacr", "\u{0100}"), (b"And", "\u{2A53}"), (b"Aogon", "\u{0104}"), (b"Aopf", "\u{1D538}"), (b"ApplyFunction", "\u{2061}"), (b"Aring", "\u{00C5}"), (b"Ascr", "\u{1D49C}"), (b"Assign", "\u{2254}"), (b"Atilde", "\u{00C3}"), (b"Auml", "\u{00C4}"), (b"Backslash", "\u{2216}"), (b"Barv", "\u{2AE7}"), (b"Barwed", "\u{2306}"), (b"Bcy", "\u{0411}"), (b"Because", "\u{2235}"), (b"Bernoullis", "\u{212C}"), (b"Beta", "\u{0392}"), (b"Bfr", "\u{1D505}"), (b"Bopf", "\u{1D539}"), (b"Breve", "\u{02D8}"), (b"Bscr", "\u{212C}"), (b"Bumpeq", "\u{224E}"), (b"CHcy", "\u{0427}"), (b"COPY", "\u{00A9}"), (b"Cacute", "\u{0106}"), (b"Cap", "\u{22D2}"), (b"CapitalDifferentialD", "\u{2145}"), (b"Cayleys", "\u{212D}"), (b"Ccaron", "\u{010C}"), (b"Ccedil", "\u{00C7}"), (b"Ccirc", "\u{0108}"), (b"Cconint", "\u{2230}"), (b"Cdot", "\u{010A}"), (b"Cedilla", "\u{00B8}"), (b"CenterDot", "\u{00B7}"), (b"Cfr", "\u{212D}"), (b"Chi", "\u{03A7}"), (b"CircleDot", "\u{2299}"), (b"CircleMinus", "\u{2296}"), (b"CirclePlus", "\u{2295}"), (b"CircleTimes", "\u{2297}"), (b"ClockwiseContourIntegral", "\u{2232}"), (b"CloseCurlyDoubleQuote", "\u{201D}"), (b"CloseCurlyQuote", "\u{2019}"), (b"Colon", "\u{2237}"), (b"Colone", "\u{2A74}"), (b"Congruent", "\u{2261}"), (b"Conint", "\u{222F}"), (b"ContourIntegral", "\u{222E}"), (b"Copf", "\u{2102}"), (b"Coproduct", "\u{2210}"), (b"CounterClockwiseContourIntegral", "\u{2233}"), (b"Cross", "\u{2A2F}"), (b"Cscr", "\u{1D49E}"), (b"Cup", "\u{22D3}"), (b"CupCap", "\u{224D}"), (b"DD", "\u{2145}"), (b"DDotrahd", "\u{2911}"), (b"DJcy", "\u{0402}"), (b"DScy", "\u{0405}"), (b"DZcy", "\u{040F}"), (b"Dagger", "\u{2021}"), (b"Darr", "\u{21A1}"), (b"Dashv", "\u{2AE4}"), (b"Dcaron", "\u{010E}"), (b"Dcy", "\u{0414}"), (b"Del", "\u{2207}"), (b"Delta", "\u{0394}"), (b"Dfr", "\u{1D507}"), (b"DiacriticalAcute", "\u{00B4}"), (b"DiacriticalDot", "\u{02D9}"), (b"DiacriticalDoubleAcute", "\u{02DD}"), (b"DiacriticalGrave", "\u{0060}"), (b"DiacriticalTilde", "\u{02DC}"), (b"Diamond", "\u{22C4}"), (b"DifferentialD", "\u{2146}"), (b"Dopf", "\u{1D53B}"), (b"Dot", "\u{00A8}"), (b"DotDot", "\u{20DC}"), (b"DotEqual", "\u{2250}"), (b"DoubleContourIntegral", "\u{222F}"), (b"DoubleDot", "\u{00A8}"), (b"DoubleDownArrow", "\u{21D3}"), (b"DoubleLeftArrow", "\u{21D0}"), (b"DoubleLeftRightArrow", "\u{21D4}"), (b"DoubleLeftTee", "\u{2AE4}"), (b"DoubleLongLeftArrow", "\u{27F8}"), (b"DoubleLongLeftRightArrow", "\u{27FA}"), (b"DoubleLongRightArrow", "\u{27F9}"), (b"DoubleRightArrow", "\u{21D2}"), (b"DoubleRightTee", "\u{22A8}"), (b"DoubleUpArrow", "\u{21D1}"), (b"DoubleUpDownArrow", "\u{21D5}"), (b"DoubleVerticalBar", "\u{2225}"), (b"DownArrow", "\u{2193}"), (b"DownArrowBar", "\u{2913}"), (b"DownArrowUpArrow", "\u{21F5}"), (b"DownBreve", "\u{0311}"), (b"DownLeftRightVector", "\u{2950}"), (b"DownLeftTeeVector", "\u{295E}"), (b"DownLeftVector", "\u{21BD}"), (b"DownLeftVectorBar", "\u{2956}"), (b"DownRightTeeVector", "\u{295F}"), (b"DownRightVector", "\u{21C1}"), (b"DownRightVectorBar", "\u{2957}"), (b"DownTee", "\u{22A4}"), (b"DownTeeArrow", "\u{21A7}"), (b"Downarrow", "\u{21D3}"), (b"Dscr", "\u{1D49F}"), (b"Dstrok", "\u{0110}"), (b"ENG", "\u{014A}"), (b"ETH", "\u{00D0}"), (b"Eacute", "\u{00C9}"), (b"Ecaron", "\u{011A}"), (b"Ecirc", "\u{00CA}"), (b"Ecy", "\u{042D}"), (b"Edot", "\u{0116}"), (b"Efr", "\u{1D508}"), (b"Egrave", "\u{00C8}"), (b"Element", "\u{2208}"), (b"Emacr", "\u{0112}"), (b"EmptySmallSquare", "\u{25FB}"), (b"EmptyVerySmallSquare", "\u{25AB}"), (b"Eogon", "\u{0118}"), (b"Eopf", "\u{1D53C}"), (b"Epsilon", "\u{0395}"), (b"Equal", "\u{2A75}"), (b"EqualTilde", "\u{2242}"), (b"Equilibrium", "\u{21CC}"), (b"Escr", "\u{2130}"), (b"Esim", "\u{2A73}"), (b"Eta", "\u{0397}"), (b"Euml", "\u{00CB}"), (b"Exists", "\u{2203}"), (b"ExponentialE", "\u{2147}"), (b"Fcy", "\u{0424}"), (b"Ffr", "\u{1D509}"), (b"FilledSmallSquare", "\u{25FC}"), (b"FilledVerySmallSquare", "\u{25AA}"), (b"Fopf", "\u{1D53D}"), (b"ForAll", "\u{2200}"), (b"Fouriertrf", "\u{2131}"), (b"Fscr", "\u{2131}"), (b"GJcy", "\u{0403}"), (b"GT", "\u{003E}"), (b"Gamma", "\u{0393}"), (b"Gammad", "\u{03DC}"), (b"Gbreve", "\u{011E}"), (b"Gcedil", "\u{0122}"), (b"Gcirc", "\u{011C}"), (b"Gcy", "\u{0413}"), (b"Gdot", "\u{0120}"), (b"Gfr", "\u{1D50A}"), (b"Gg", "\u{22D9}"), (b"Gopf", "\u{1D53E}"), (b"GreaterEqual", "\u{2265}"), (b"GreaterEqualLess", "\u{22DB}"), (b"GreaterFullEqual", "\u{2267}"), (b"GreaterGreater", "\u{2AA2}"), (b"GreaterLess", "\u{2277}"), (b"GreaterSlantEqual", "\u{2A7E}"), (b"GreaterTilde", "\u{2273}"), (b"Gscr", "\u{1D4A2}"), (b"Gt", "\u{226B}"), (b"HARDcy", "\u{042A}"), (b"Hacek", "\u{02C7}"), (b"Hat", "\u{005E}"), (b"Hcirc", "\u{0124}"), (b"Hfr", "\u{210C}"), (b"HilbertSpace", "\u{210B}"), (b"Hopf", "\u{210D}"), (b"HorizontalLine", "\u{2500}"), (b"Hscr", "\u{210B}"), (b"Hstrok", "\u{0126}"), (b"HumpDownHump", "\u{224E}"), (b"HumpEqual", "\u{224F}"), (b"IEcy", "\u{0415}"), (b"IJlig", "\u{0132}"), (b"IOcy", "\u{0401}"), (b"Iacute", "\u{00CD}"), (b"Icirc", "\u{00CE}"), (b"Icy", "\u{0418}"), (b"Idot", "\u{0130}"), (b"Ifr", "\u{2111}"), (b"Igrave", "\u{00CC}"), (b"Im", "\u{2111}"), (b"Imacr", "\u{012A}"), (b"ImaginaryI", "\u{2148}"), (b"Implies", "\u{21D2}"), (b"Int", "\u{222C}"), (b"Integral", "\u{222B}"), (b"Intersection", "\u{22C2}"), (b"InvisibleComma", "\u{2063}"), (b"InvisibleTimes", "\u{2062}"), (b"Iogon", "\u{012E}"), (b"Iopf", "\u{1D540}"), (b"Iota", "\u{0399}"), (b"Iscr", "\u{2110}"), (b"Itilde", "\u{0128}"), (b"Iukcy", "\u{0406}"), (b"Iuml", "\u{00CF}"), (b"Jcirc", "\u{0134}"), (b"Jcy", "\u{0419}"), (b"Jfr", "\u{1D50D}"), (b"Jopf", "\u{1D541}"), (b"Jscr", "\u{1D4A5}"), (b"Jsercy", "\u{0408}"), (b"Jukcy", "\u{0404}"), (b"KHcy", "\u{0425}"), (b"KJcy", "\u{040C}"), (b"Kappa", "\u{039A}"), (b"Kcedil", "\u{0136}"), (b"Kcy", "\u{041A}"), (b"Kfr", "\u{1D50E}"), (b"Kopf", "\u{1D542}"), (b"Kscr", "\u{1D4A6}"), (b"LJcy", "\u{0409}"), (b"LT", "\u{003C}"), (b"Lacute", "\u{0139}"), (b"Lambda", "\u{039B}"), (b"Lang", "\u{27EA}"), (b"Laplacetrf", "\u{2112}"), (b"Larr", "\u{219E}"), (b"Lcaron", "\u{013D}"), (b"Lcedil", "\u{013B}"), (b"Lcy", "\u{041B}"), (b"LeftAngleBracket", "\u{27E8}"), (b"LeftArrow", "\u{2190}"), (b"LeftArrowBar", "\u{21E4}"), (b"LeftArrowRightArrow", "\u{21C6}"), (b"LeftCeiling", "\u{2308}"), (b"LeftDoubleBracket", "\u{27E6}"), (b"LeftDownTeeVector", "\u{2961}"), (b"LeftDownVector", "\u{21C3}"), (b"LeftDownVectorBar", "\u{2959}"), (b"LeftFloor", "\u{230A}"), (b"LeftRightArrow", "\u{2194}"), (b"LeftRightVector", "\u{294E}"), (b"LeftTee", "\u{22A3}"), (b"LeftTeeArrow", "\u{21A4}"), (b"LeftTeeVector", "\u{295A}"), (b"LeftTriangle", "\u{22B2}"), (b"LeftTriangleBar", "\u{29CF}"), (b"LeftTriangleEqual", "\u{22B4}"), (b"LeftUpDownVector", "\u{2951}"), (b"LeftUpTeeVector", "\u{2960}"), (b"LeftUpVector", "\u{21BF}"), (b"LeftUpVectorBar", "\u{2958}"), (b"LeftVector", "\u{21BC}"), (b"LeftVectorBar", "\u{2952}"), (b"Leftarrow", "\u{21D0}"), (b"Leftrightarrow", "\u{21D4}"), (b"LessEqualGreater", "\u{22DA}"), (b"LessFullEqual", "\u{2266}"), (b"LessGreater", "\u{2276}"), (b"LessLess", "\u{2AA1}"), (b"LessSlantEqual", "\u{2A7D}"), (b"LessTilde", "\u{2272}"), (b"Lfr", "\u{1D50F}"), (b"Ll", "\u{22D8}"), (b"Lleftarrow", "\u{21DA}"), (b"Lmidot", "\u{013F}"), (b"LongLeftArrow", "\u{27F5}"), (b"LongLeftRightArrow", "\u{27F7}"), (b"LongRightArrow", "\u{27F6}"), (b"Longleftarrow", "\u{27F8}"), (b"Longleftrightarrow", "\u{27FA}"), (b"Longrightarrow", "\u{27F9}"), (b"Lopf", "\u{1D543}"), (b"LowerLeftArrow", "\u{2199}"), (b"LowerRightArrow", "\u{2198}"), (b"Lscr", "\u{2112}"), (b"Lsh", "\u{21B0}"), (b"Lstrok", "\u{0141}"), (b"Lt", "\u{226A}"), (b"Map", "\u{2905}"), (b"Mcy", "\u{041C}"), (b"MediumSpace", "\u{205F}"), (b"Mellintrf", "\u{2133}"), (b"Mfr", "\u{1D510}"), (b"MinusPlus", "\u{2213}"), (b"Mopf", "\u{1D544}"), (b"Mscr", "\u{2133}"), (b"Mu", "\u{039C}"), (b"NJcy", "\u{040A}"), (b"Nacute", "\u{0143}"), (b"Ncaron", "\u{0147}"), (b"Ncedil", "\u{0145}"), (b"Ncy", "\u{041D}"), (b"NegativeMediumSpace", "\u{200B}"), (b"NegativeThickSpace", "\u{200B}"), (b"NegativeThinSpace", "\u{200B}"), (b"NegativeVeryThinSpace", "\u{200B}"), (b"NestedGreaterGreater", "\u{226B}"), (b"NestedLessLess", "\u{226A}"), (b"NewLine", "\u{000A}"), (b"Nfr", "\u{1D511}"), (b"NoBreak", "\u{2060}"), (b"NonBreakingSpace", "\u{00A0}"), (b"Nopf", "\u{2115}"), (b"Not", "\u{2AEC}"), (b"NotCongruent", "\u{2262}"), (b"NotCupCap", "\u{226D}"), (b"NotDoubleVerticalBar", "\u{2226}"), (b"NotElement", "\u{2209}"), (b"NotEqual", "\u{2260}"), (b"NotEqualTilde", "\u{2242}\u{0338}"), (b"NotExists", "\u{2204}"), (b"NotGreater", "\u{226F}"), (b"NotGreaterEqual", "\u{2271}"), (b"NotGreaterFullEqual", "\u{2267}\u{0338}"), (b"NotGreaterGreater", "\u{226B}\u{0338}"), (b"NotGreaterLess", "\u{2279}"), (b"NotGreaterSlantEqual", "\u{2A7E}\u{0338}"), (b"NotGreaterTilde", "\u{2275}"), (b"NotHumpDownHump", "\u{224E}\u{0338}"), (b"NotHumpEqual", "\u{224F}\u{0338}"), (b"NotLeftTriangle", "\u{22EA}"), (b"NotLeftTriangleBar", "\u{29CF}\u{0338}"), (b"NotLeftTriangleEqual", "\u{22EC}"), (b"NotLess", "\u{226E}"), (b"NotLessEqual", "\u{2270}"), (b"NotLessGreater", "\u{2278}"), (b"NotLessLess", "\u{226A}\u{0338}"), (b"NotLessSlantEqual", "\u{2A7D}\u{0338}"), (b"NotLessTilde", "\u{2274}"), (b"NotNestedGreaterGreater", "\u{2AA2}\u{0338}"), (b"NotNestedLessLess", "\u{2AA1}\u{0338}"), (b"NotPrecedes", "\u{2280}"), (b"NotPrecedesEqual", "\u{2AAF}\u{0338}"), (b"NotPrecedesSlantEqual", "\u{22E0}"), (b"NotReverseElement", "\u{220C}"), (b"NotRightTriangle", "\u{22EB}"), (b"NotRightTriangleBar", "\u{29D0}\u{0338}"), (b"NotRightTriangleEqual", "\u{22ED}"), (b"NotSquareSubset", "\u{228F}\u{0338}"), (b"NotSquareSubsetEqual", "\u{22E2}"), (b"NotSquareSuperset", "\u{2290}\u{0338}"), (b"NotSquareSupersetEqual", "\u{22E3}"), (b"NotSubset", "\u{2282}\u{20D2}"), (b"NotSubsetEqual", "\u{2288}"), (b"NotSucceeds", "\u{2281}"), (b"NotSucceedsEqual", "\u{2AB0}\u{0338}"), (b"NotSucceedsSlantEqual", "\u{22E1}"), (b"NotSucceedsTilde", "\u{227F}\u{0338}"), (b"NotSuperset", "\u{2283}\u{20D2}"), (b"NotSupersetEqual", "\u{2289}"), (b"NotTilde", "\u{2241}"), (b"NotTildeEqual", "\u{2244}"), (b"NotTildeFullEqual", "\u{2247}"), (b"NotTildeTilde", "\u{2249}"), (b"NotVerticalBar", "\u{2224}"), (b"Nscr", "\u{1D4A9}"), (b"Ntilde", "\u{00D1}"), (b"Nu", "\u{039D}"), (b"OElig", "\u{0152}"), (b"Oacute", "\u{00D3}"), (b"Ocirc", "\u{00D4}"), (b"Ocy", "\u{041E}"), (b"Odblac", "\u{0150}"), (b"Ofr", "\u{1D512}"), (b"Ograve", "\u{00D2}"), (b"Omacr", "\u{014C}"), (b"Omega", "\u{03A9}"), (b"Omicron", "\u{039F}"), (b"Oopf", "\u{1D546}"), (b"OpenCurlyDoubleQuote", "\u{201C}"), (b"OpenCurlyQuote", "\u{2018}"), (b"Or", "\u{2A54}"), (b"Oscr", "\u{1D4AA}"), (b"Oslash", "\u{00D8}"), (b"Otilde", "\u{00D5}"), (b"Otimes", "\u{2A37}"), (b"Ouml", "\u{00D6}"), (b"OverBar", "\u{203E}"), (b"OverBrace", "\u{23DE}"), (b"OverBracket", "\u{23B4}"), (b"OverParenthesis", "\u{23DC}"), (b"PartialD", "\u{2202}"), (b"Pcy", "\u{041F}"), (b"Pfr", "\u{1D513}"), (b"Phi", "\u{03A6}"), (b"Pi", "\u{03A0}"), (b"PlusMinus", "\u{00B1}"), (b"Poincareplane", "\u{210C}"), (b"Popf", "\u{2119}"), (b"Pr", "\u{2ABB}"), (b"Precedes", "\u{227A}"), (b"PrecedesEqual", "\u{2AAF}"), (b"PrecedesSlantEqual", "\u{227C}"), (b"PrecedesTilde", "\u{227E}"), (b"Prime", "\u{2033}"), (b"Product", "\u{220F}"), (b"Proportion", "\u{2237}"), (b"Proportional", "\u{221D}"), (b"Pscr", "\u{1D4AB}"), (b"Psi", "\u{03A8}"), (b"QUOT", "\u{0022}"), (b"Qfr", "\u{1D514}"), (b"Qopf", "\u{211A}"), (b"Qscr", "\u{1D4AC}"), (b"RBarr", "\u{2910}"), (b"REG", "\u{00AE}"), (b"Racute", "\u{0154}"), (b"Rang", "\u{27EB}"), (b"Rarr", "\u{21A0}"), (b"Rarrtl", "\u{2916}"), (b"Rcaron", "\u{0158}"), (b"Rcedil", "\u{0156}"), (b"Rcy", "\u{0420}"), (b"Re", "\u{211C}"), (b"ReverseElement", "\u{220B}"), (b"ReverseEquilibrium", "\u{21CB}"), (b"ReverseUpEquilibrium", "\u{296F}"), (b"Rfr", "\u{211C}"), (b"Rho", "\u{03A1}"), (b"RightAngleBracket", "\u{27E9}"), (b"RightArrow", "\u{2192}"), (b"RightArrowBar", "\u{21E5}"), (b"RightArrowLeftArrow", "\u{21C4}"), (b"RightCeiling", "\u{2309}"), (b"RightDoubleBracket", "\u{27E7}"), (b"RightDownTeeVector", "\u{295D}"), (b"RightDownVector", "\u{21C2}"), (b"RightDownVectorBar", "\u{2955}"), (b"RightFloor", "\u{230B}"), (b"RightTee", "\u{22A2}"), (b"RightTeeArrow", "\u{21A6}"), (b"RightTeeVector", "\u{295B}"), (b"RightTriangle", "\u{22B3}"), (b"RightTriangleBar", "\u{29D0}"), (b"RightTriangleEqual", "\u{22B5}"), (b"RightUpDownVector", "\u{294F}"), (b"RightUpTeeVector", "\u{295C}"), (b"RightUpVector", "\u{21BE}"), (b"RightUpVectorBar", "\u{2954}"), (b"RightVector", "\u{21C0}"), (b"RightVectorBar", "\u{2953}"), (b"Rightarrow", "\u{21D2}"), (b"Ropf", "\u{211D}"), (b"RoundImplies", "\u{2970}"), (b"Rrightarrow", "\u{21DB}"), (b"Rscr", "\u{211B}"), (b"Rsh", "\u{21B1}"), (b"RuleDelayed", "\u{29F4}"), (b"SHCHcy", "\u{0429}"), (b"SHcy", "\u{0428}"), (b"SOFTcy", "\u{042C}"), (b"Sacute", "\u{015A}"), (b"Sc", "\u{2ABC}"), (b"Scaron", "\u{0160}"), (b"Scedil", "\u{015E}"), (b"Scirc", "\u{015C}"), (b"Scy", "\u{0421}"), (b"Sfr", "\u{1D516}"), (b"ShortDownArrow", "\u{2193}"), (b"ShortLeftArrow", "\u{2190}"), (b"ShortRightArrow", "\u{2192}"), (b"ShortUpArrow", "\u{2191}"), (b"Sigma", "\u{03A3}"), (b"SmallCircle", "\u{2218}"), (b"Sopf", "\u{1D54A}"), (b"Sqrt", "\u{221A}"), (b"Square", "\u{25A1}"), (b"SquareIntersection", "\u{2293}"), (b"SquareSubset", "\u{228F}"), (b"SquareSubsetEqual", "\u{2291}"), (b"SquareSuperset", "\u{2290}"), (b"SquareSupersetEqual", "\u{2292}"), (b"SquareUnion", "\u{2294}"), (b"Sscr", "\u{1D4AE}"), (b"Star", "\u{22C6}"), (b"Sub", "\u{22D0}"), (b"Subset", "\u{22D0}"), (b"SubsetEqual", "\u{2286}"), (b"Succeeds", "\u{227B}"), (b"SucceedsEqual", "\u{2AB0}"), (b"SucceedsSlantEqual", "\u{227D}"), (b"SucceedsTilde", "\u{227F}"), (b"SuchThat", "\u{220B}"), (b"Sum", "\u{2211}"), (b"Sup", "\u{22D1}"), (b"Superset", "\u{2283}"), (b"SupersetEqual", "\u{2287}"), (b"Supset", "\u{22D1}"), (b"THORN", "\u{00DE}"), (b"TRADE", "\u{2122}"), (b"TSHcy", "\u{040B}"), (b"TScy", "\u{0426}"), (b"Tab", "\u{0009}"), (b"Tau", "\u{03A4}"), (b"Tcaron", "\u{0164}"), (b"Tcedil", "\u{0162}"), (b"Tcy", "\u{0422}"), (b"Tfr", "\u{1D517}"), (b"Therefore", "\u{2234}"), (b"Theta", "\u{0398}"), (b"ThickSpace", "\u{205F}\u{200A}"), (b"ThinSpace", "\u{2009}"), (b"Tilde", "\u{223C}"), (b"TildeEqual", "\u{2243}"), (b"TildeFullEqual", "\u{2245}"), (b"TildeTilde", "\u{2248}"), (b"Topf", "\u{1D54B}"), (b"TripleDot", "\u{20DB}"), (b"Tscr", "\u{1D4AF}"), (b"Tstrok", "\u{0166}"), (b"Uacute", "\u{00DA}"), (b"Uarr", "\u{219F}"), (b"Uarrocir", "\u{2949}"), (b"Ubrcy", "\u{040E}"), (b"Ubreve", "\u{016C}"), (b"Ucirc", "\u{00DB}"), (b"Ucy", "\u{0423}"), (b"Udblac", "\u{0170}"), (b"Ufr", "\u{1D518}"), (b"Ugrave", "\u{00D9}"), (b"Umacr", "\u{016A}"), (b"UnderBar", "\u{005F}"), (b"UnderBrace", "\u{23DF}"), (b"UnderBracket", "\u{23B5}"), (b"UnderParenthesis", "\u{23DD}"), (b"Union", "\u{22C3}"), (b"UnionPlus", "\u{228E}"), (b"Uogon", "\u{0172}"), (b"Uopf", "\u{1D54C}"), (b"UpArrow", "\u{2191}"), (b"UpArrowBar", "\u{2912}"), (b"UpArrowDownArrow", "\u{21C5}"), (b"UpDownArrow", "\u{2195}"), (b"UpEquilibrium", "\u{296E}"), (b"UpTee", "\u{22A5}"), (b"UpTeeArrow", "\u{21A5}"), (b"Uparrow", "\u{21D1}"), (b"Updownarrow", "\u{21D5}"), (b"UpperLeftArrow", "\u{2196}"), (b"UpperRightArrow", "\u{2197}"), (b"Upsi", "\u{03D2}"), (b"Upsilon", "\u{03A5}"), (b"Uring", "\u{016E}"), (b"Uscr", "\u{1D4B0}"), (b"Utilde", "\u{0168}"), (b"Uuml", "\u{00DC}"), (b"VDash", "\u{22AB}"), (b"Vbar", "\u{2AEB}"), (b"Vcy", "\u{0412}"), (b"Vdash", "\u{22A9}"), (b"Vdashl", "\u{2AE6}"), (b"Vee", "\u{22C1}"), (b"Verbar", "\u{2016}"), (b"Vert", "\u{2016}"), (b"VerticalBar", "\u{2223}"), (b"VerticalLine", "\u{007C}"), (b"VerticalSeparator", "\u{2758}"), (b"VerticalTilde", "\u{2240}"), (b"VeryThinSpace", "\u{200A}"), (b"Vfr", "\u{1D519}"), (b"Vopf", "\u{1D54D}"), (b"Vscr", "\u{1D4B1}"), (b"Vvdash", "\u{22AA}"), (b"Wcirc", "\u{0174}"), (b"Wedge", "\u{22C0}"), (b"Wfr", "\u{1D51A}"), (b"Wopf", "\u{1D54E}"), (b"Wscr", "\u{1D4B2}"), (b"Xfr", "\u{1D51B}"), (b"Xi", "\u{039E}"), (b"Xopf", "\u{1D54F}"), (b"Xscr", "\u{1D4B3}"), (b"YAcy", "\u{042F}"), (b"YIcy", "\u{0407}"), (b"YUcy", "\u{042E}"), (b"Yacute", "\u{00DD}"), (b"Ycirc", "\u{0176}"), (b"Ycy", "\u{042B}"), (b"Yfr", "\u{1D51C}"), (b"Yopf", "\u{1D550}"), (b"Yscr", "\u{1D4B4}"), (b"Yuml", "\u{0178}"), (b"ZHcy", "\u{0416}"), (b"Zacute", "\u{0179}"), (b"Zcaron", "\u{017D}"), (b"Zcy", "\u{0417}"), (b"Zdot", "\u{017B}"), (b"ZeroWidthSpace", "\u{200B}"), (b"Zeta", "\u{0396}"), (b"Zfr", "\u{2128}"), (b"Zopf", "\u{2124}"), (b"Zscr", "\u{1D4B5}"), (b"aacute", "\u{00E1}"), (b"abreve", "\u{0103}"), (b"ac", "\u{223E}"), (b"acE", "\u{223E}\u{0333}"), (b"acd", "\u{223F}"), (b"acirc", "\u{00E2}"), (b"acute", "\u{00B4}"), (b"acy", "\u{0430}"), (b"aelig", "\u{00E6}"), (b"af", "\u{2061}"), (b"afr", "\u{1D51E}"), (b"agrave", "\u{00E0}"), (b"alefsym", "\u{2135}"), (b"aleph", "\u{2135}"), (b"alpha", "\u{03B1}"), (b"amacr", "\u{0101}"), (b"amalg", "\u{2A3F}"), (b"amp", "\u{0026}"), (b"and", "\u{2227}"), (b"andand", "\u{2A55}"), (b"andd", "\u{2A5C}"), (b"andslope", "\u{2A58}"), (b"andv", "\u{2A5A}"), (b"ang", "\u{2220}"), (b"ange", "\u{29A4}"), (b"angle", "\u{2220}"), (b"angmsd", "\u{2221}"), (b"angmsdaa", "\u{29A8}"), (b"angmsdab", "\u{29A9}"), (b"angmsdac", "\u{29AA}"), (b"angmsdad", "\u{29AB}"), (b"angmsdae", "\u{29AC}"), (b"angmsdaf", "\u{29AD}"), (b"angmsdag", "\u{29AE}"), (b"angmsdah", "\u{29AF}"), (b"angrt", "\u{221F}"), (b"angrtvb", "\u{22BE}"), (b"angrtvbd", "\u{299D}"), (b"angsph", "\u{2222}"), (b"angst", "\u{00C5}"), (b"angzarr", "\u{237C}"), (b"aogon", "\u{0105}"), (b"aopf", "\u{1D552}"), (b"ap", "\u{2248}"), (b"apE", "\u{2A70}"), (b"apacir", "\u{2A6F}"), (b"ape", "\u{224A}"), (b"apid", "\u{224B}"), (b"apos", "\u{0027}"), (b"approx", "\u{2248}"), (b"approxeq", "\u{224A}"), (b"aring", "\u{00E5}"), (b"ascr", "\u{1D4B6}"), (b"ast", "\u{002A}"), (b"asymp", "\u{2248}"), (b"asympeq", "\u{224D}"), (b"atilde", "\u{00E3}"), (b"auml", "\u{00E4}"), (b"awconint", "\u{2233}"), (b"awint", "\u{2A11}"), (b"bNot", "\u{2AED}"), (b"backcong", "\u{224C}"), (b"backepsilon", "\u{03F6}"), (b"backprime", "\u{2035}"), (b"backsim", "\u{223D}"), (b"backsimeq", "\u{22CD}"), (b"barvee", "\u{22BD}"), (b"barwed", "\u{2305}"), (b"barwedge", "\u{2305}"), (b"bbrk", "\u{23B5}"), (b"bbrktbrk", "\u{23B6}"), (b"bcong", "\u{224C}"), (b"bcy", "\u{0431}"), (b"bdquo", "\u{201E}"), (b"becaus", "\u{2235}"), (b"because", "\u{2235}"), (b"bemptyv", "\u{29B0}"), (b"bepsi", "\u{03F6}"), (b"bernou", "\u{212C}"), (b"beta", "\u{03B2}"), (b"beth", "\u{2136}"), (b"between", "\u{226C}"), (b"bfr", "\u{1D51F}"), (b"bigcap", "\u{22C2}"), (b"bigcirc", "\u{25EF}"), (b"bigcup", "\u{22C3}"), (b"bigodot", "\u{2A00}"), (b"bigoplus", "\u{2A01}"), (b"bigotimes", "\u{2A02}"), (b"bigsqcup", "\u{2A06}"), (b"bigstar", "\u{2605}"), (b"bigtriangledown", "\u{25BD}"), (b"bigtriangleup", "\u{25B3}"), (b"biguplus", "\u{2A04}"), (b"bigvee", "\u{22C1}"), (b"bigwedge", "\u{22C0}"), (b"bkarow", "\u{290D}"), (b"blacklozenge", "\u{29EB}"), (b"blacksquare", "\u{25AA}"), (b"blacktriangle", "\u{25B4}"), (b"blacktriangledown", "\u{25BE}"), (b"blacktriangleleft", "\u{25C2}"), (b"blacktriangleright", "\u{25B8}"), (b"blank", "\u{2423}"), (b"blk12", "\u{2592}"), (b"blk14", "\u{2591}"), (b"blk34", "\u{2593}"), (b"block", "\u{2588}"), (b"bne", "\u{003D}\u{20E5}"), (b"bnequiv", "\u{2261}\u{20E5}"), (b"bnot", "\u{2310}"), (b"bopf", "\u{1D553}"), (b"bot", "\u{22A5}"), (b"bottom", "\u{22A5}"), (b"bowtie", "\u{22C8}"), (b"boxDL", "\u{2557}"), (b"boxDR", "\u{2554}"), (b"boxDl", "\u{2556}"), (b"boxDr", "\u{2553}"), (b"boxH", "\u{2550}"), (b"boxHD", "\u{2566}"), (b"boxHU", "\u{2569}"), (b"boxHd", "\u{2564}"), (b"boxHu", "\u{2567}"), (b"boxUL", "\u{255D}"), (b"boxUR", "\u{255A}"), (b"boxUl", "\u{255C}"), (b"boxUr", "\u{2559}"), (b"boxV", "\u{2551}"), (b"boxVH", "\u{256C}"), (b"boxVL", "\u{2563}"), (b"boxVR", "\u{2560}"), (b"boxVh", "\u{256B}"), (b"boxVl", "\u{2562}"), (b"boxVr", "\u{255F}"), (b"boxbox", "\u{29C9}"), (b"boxdL", "\u{2555}"), (b"boxdR", "\u{2552}"), (b"boxdl", "\u{2510}"), (b"boxdr", "\u{250C}"), (b"boxh", "\u{2500}"), (b"boxhD", "\u{2565}"), (b"boxhU", "\u{2568}"), (b"boxhd", "\u{252C}"), (b"boxhu", "\u{2534}"), (b"boxminus", "\u{229F}"), (b"boxplus", "\u{229E}"), (b"boxtimes", "\u{22A0}"), (b"boxuL", "\u{255B}"), (b"boxuR", "\u{2558}"), (b"boxul", "\u{2518}"), (b"boxur", "\u{2514}"), (b"boxv", "\u{2502}"), (b"boxvH", "\u{256A}"), (b"boxvL", "\u{2561}"), (b"boxvR", "\u{255E}"), (b"boxvh", "\u{253C}"), (b"boxvl", "\u{2524}"), (b"boxvr", "\u{251C}"), (b"bprime", "\u{2035}"), (b"breve", "\u{02D8}"), (b"brvbar", "\u{00A6}"), (b"bscr", "\u{1D4B7}"), (b"bsemi", "\u{204F}"), (b"bsim", "\u{223D}"), (b"bsime", "\u{22CD}"), (b"bsol", "\u{005C}"), (b"bsolb", "\u{29C5}"), (b"bsolhsub", "\u{27C8}"), (b"bull", "\u{2022}"), (b"bullet", "\u{2022}"), (b"bump", "\u{224E}"), (b"bumpE", "\u{2AAE}"), (b"bumpe", "\u{224F}"), (b"bumpeq", "\u{224F}"), (b"cacute", "\u{0107}"), (b"cap", "\u{2229}"), (b"capand", "\u{2A44}"), (b"capbrcup", "\u{2A49}"), (b"capcap", "\u{2A4B}"), (b"capcup", "\u{2A47}"), (b"capdot", "\u{2A40}"), (b"caps", "\u{2229}\u{FE00}"), (b"caret", "\u{2041}"), (b"caron", "\u{02C7}"), (b"ccaps", "\u{2A4D}"), (b"ccaron", "\u{010D}"), (b"ccedil", "\u{00E7}"), (b"ccirc", "\u{0109}"), (b"ccups", "\u{2A4C}"), (b"ccupssm", "\u{2A50}"), (b"cdot", "\u{010B}"), (b"cedil", "\u{00B8}"), (b"cemptyv", "\u{29B2}"), (b"cent", "\u{00A2}"), (b"centerdot", "\u{00B7}"), (b"cfr", "\u{1D520}"), (b"chcy", "\u{0447}"), (b"check", "\u{2713}"), (b"checkmark", "\u{2713}"), (b"chi", "\u{03C7}"), (b"cir", "\u{25CB}"), (b"cirE", "\u{29C3}"), (b"circ", "\u{02C6}"), (b"circeq", "\u{2257}"), (b"circlearrowleft", "\u{21BA}"), (b"circlearrowright", "\u{21BB}"), (b"circledR", "\u{00AE}"), (b"circledS", "\u{24C8}"), (b"circledast", "\u{229B}"), (b"circledcirc", "\u{229A}"), (b"circleddash", "\u{229D}"), (b"cire", "\u{2257}"), (b"cirfnint", "\u{2A10}"), (b"cirmid", "\u{2AEF}"), (b"cirscir", "\u{29C2}"), (b"clubs", "\u{2663}"), (b"clubsuit", "\u{2663}"), (b"colon", "\u{003A}"), (b"colone", "\u{2254}"), (b"coloneq", "\u{2254}"), (b"comma", "\u{002C}"), (b"commat", "\u{0040}"), (b"comp", "\u{2201}"), (b"compfn", "\u{2218}"), (b"complement", "\u{2201}"), (b"complexes", "\u{2102}"), (b"cong", "\u{2245}"), (b"congdot", "\u{2A6D}"), (b"conint", "\u{222E}"), (b"copf", "\u{1D554}"), (b"coprod", "\u{2210}"), (b"copy", "\u{00A9}"), (b"copysr", "\u{2117}"), (b"crarr", "\u{21B5}"), (b"cross", "\u{2717}"), (b"cscr", "\u{1D4B8}"), (b"csub", "\u{2ACF}"), (b"csube", "\u{2AD1}"), (b"csup", "\u{2AD0}"), (b"csupe", "\u{2AD2}"), (b"ctdot", "\u{22EF}"), (b"cudarrl", "\u{2938}"), (b"cudarrr", "\u{2935}"), (b"cuepr", "\u{22DE}"), (b"cuesc", "\u{22DF}"), (b"cularr", "\u{21B6}"), (b"cularrp", "\u{293D}"), (b"cup", "\u{222A}"), (b"cupbrcap", "\u{2A48}"), (b"cupcap", "\u{2A46}"), (b"cupcup", "\u{2A4A}"), (b"cupdot", "\u{228D}"), (b"cupor", "\u{2A45}"), (b"cups", "\u{222A}\u{FE00}"), (b"curarr", "\u{21B7}"), (b"curarrm", "\u{293C}"), (b"curlyeqprec", "\u{22DE}"), (b"curlyeqsucc", "\u{22DF}"), (b"curlyvee", "\u{22CE}"), (b"curlywedge", "\u{22CF}"), (b"curren", "\u{00A4}"), (b"curvearrowleft", "\u{21B6}"), (b"curvearrowright", "\u{21B7}"), (b"cuvee", "\u{22CE}"), (b"cuwed", "\u{22CF}"), (b"cwconint", "\u{2232}"), (b"cwint", "\u{2231}"), (b"cylcty", "\u{232D}"), (b"dArr", "\u{21D3}"), (b"dHar", "\u{2965}"), (b"dagger", "\u{2020}"), (b"daleth", "\u{2138}"), (b"darr", "\u{2193}"), (b"dash", "\u{2010}"), (b"dashv", "\u{22A3}"), (b"dbkarow", "\u{290F}"), (b"dblac", "\u{02DD}"), (b"dcaron", "\u{010F}"), (b"dcy", "\u{0434}"), (b"dd", "\u{2146}"), (b"ddagger", "\u{2021}"), (b"ddarr", "\u{21CA}"), (b"ddotseq", "\u{2A77}"), (b"deg", "\u{00B0}"), (b"delta", "\u{03B4}"), (b"demptyv", "\u{29B1}"), (b"dfisht", "\u{297F}"), (b"dfr", "\u{1D521}"), (b"dharl", "\u{21C3}"), (b"dharr", "\u{21C2}"), (b"diam", "\u{22C4}"), (b"diamond", "\u{22C4}"), (b"diamondsuit", "\u{2666}"), (b"diams", "\u{2666}"), (b"die", "\u{00A8}"), (b"digamma", "\u{03DD}"), (b"disin", "\u{22F2}"), (b"div", "\u{00F7}"), (b"divide", "\u{00F7}"), (b"divideontimes", "\u{22C7}"), (b"divonx", "\u{22C7}"), (b"djcy", "\u{0452}"), (b"dlcorn", "\u{231E}"), (b"dlcrop", "\u{230D}"), (b"dollar", "\u{0024}"), (b"dopf", "\u{1D555}"), (b"dot", "\u{02D9}"), (b"doteq", "\u{2250}"), (b"doteqdot", "\u{2251}"), (b"dotminus", "\u{2238}"), (b"dotplus", "\u{2214}"), (b"dotsquare", "\u{22A1}"), (b"doublebarwedge", "\u{2306}"), (b"downarrow", "\u{2193}"), (b"downdownarrows", "\u{21CA}"), (b"downharpoonleft", "\u{21C3}"), (b"downharpoonright", "\u{21C2}"), (b"drbkarow", "\u{2910}"), (b"drcorn", "\u{231F}"), (b"drcrop", "\u{230C}"), (b"dscr", "\u{1D4B9}"), (b"dscy", "\u{0455}"), (b"dsol", "\u{29F6}"), (b"dstrok", "\u{0111}"), (b"dtdot", "\u{22F1}"), (b"dtri", "\u{25BF}"), (b"dtrif", "\u{25BE}"), (b"duarr", "\u{21F5}"), (b"duhar", "\u{296F}"), (b"dwangle", "\u{29A6}"), (b"dzcy", "\u{045F}"), (b"dzigrarr", "\u{27FF}"), (b"eDDot", "\u{2A77}"), (b"eDot", "\u{2251}"), (b"eacute", "\u{00E9}"), (b"easter", "\u{2A6E}"), (b"ecaron", "\u{011B}"), (b"ecir", "\u{2256}"), (b"ecirc", "\u{00EA}"), (b"ecolon", "\u{2255}"), (b"ecy", "\u{044D}"), (b"edot", "\u{0117}"), (b"ee", "\u{2147}"), (b"efDot", "\u{2252}"), (b"efr", "\u{1D522}"), (b"eg", "\u{2A9A}"), (b"egrave", "\u{00E8}"), (b"egs", "\u{2A96}"), (b"egsdot", "\u{2A98}"), (b"el", "\u{2A99}"), (b"elinters", "\u{23E7}"), (b"ell", "\u{2113}"), (b"els", "\u{2A95}"), (b"elsdot", "\u{2A97}"), (b"emacr", "\u{0113}"), (b"empty", "\u{2205}"), (b"emptyset", "\u{2205}"), (b"emptyv", "\u{2205}"), (b"emsp", "\u{2003}"), (b"emsp13", "\u{2004}"), (b"emsp14", "\u{2005}"), (b"eng", "\u{014B}"), (b"ensp", "\u{2002}"), (b"eogon", "\u{0119}"), (b"eopf", "\u{1D556}"), (b"epar", "\u{22D5}"), (b"eparsl", "\u{29E3}"), (b"eplus", "\u{2A71}"), (b"epsi", "\u{03B5}"), (b"epsilon", "\u{03B5}"), (b"epsiv", "\u{03F5}"), (b"eqcirc", "\u{2256}"), (b"eqcolon", "\u{2255}"), (b"eqsim", "\u{2242}"), (b"eqslantgtr", "\u{2A96}"), (b"eqslantless", "\u{2A95}"), (b"equals", "\u{003D}"), (b"equest", "\u{225F}"), (b"equiv", "\u{2261}"), (b"equivDD", "\u{2A78}"), (b"eqvparsl", "\u{29E5}"), (b"erDot", "\u{2253}"), (b"erarr", "\u{2971}"), (b"escr", "\u{212F}"), (b"esdot", "\u{2250}"), (b"esim", "\u{2242}"), (b"eta", "\u{03B7}"), (b"eth", "\u{00F0}"), (b"euml", "\u{00EB}"), (b"euro", "\u{20AC}"), (b"excl", "\u{0021}"), (b"exist", "\u{2203}"), (b"expectation", "\u{2130}"), (b"exponentiale", "\u{2147}"), (b"fallingdotseq", "\u{2252}"), (b"fcy", "\u{0444}"), (b"female", "\u{2640}"), (b"ffilig", "\u{FB03}"), (b"fflig", "\u{FB00}"), (b"ffllig", "\u{FB04}"), (b"ffr", "\u{1D523}"), (b"filig", "\u{FB01}"), (b"fjlig", "\u{0066}\u{006A}"), (b"flat", "\u{266D}"), (b"fllig", "\u{FB02}"), (b"fltns", "\u{25B1}"), (b"fnof", "\u{0192}"), (b"fopf", "\u{1D557}"), (b"forall", "\u{2200}"), (b"fork", "\u{22D4}"), (b"forkv", "\u{2AD9}"), (b"fpartint", "\u{2A0D}"), (b"frac12", "\u{00BD}"), (b"frac13", "\u{2153}"), (b"frac14", "\u{00BC}"), (b"frac15", "\u{2155}"), (b"frac16", "\u{2159}"), (b"frac18", "\u{215B}"), (b"frac23", "\u{2154}"), (b"frac25", "\u{2156}"), (b"frac34", "\u{00BE}"), (b"frac35", "\u{2157}"), (b"frac38", "\u{215C}"), (b"frac45", "\u{2158}"), (b"frac56", "\u{215A}"), (b"frac58", "\u{215D}"), (b"frac78", "\u{215E}"), (b"frasl", "\u{2044}"), (b"frown", "\u{2322}"), (b"fscr", "\u{1D4BB}"), (b"gE", "\u{2267}"), (b"gEl", "\u{2A8C}"), (b"gacute", "\u{01F5}"), (b"gamma", "\u{03B3}"), (b"gammad", "\u{03DD}"), (b"gap", "\u{2A86}"), (b"gbreve", "\u{011F}"), (b"gcirc", "\u{011D}"), (b"gcy", "\u{0433}"), (b"gdot", "\u{0121}"), (b"ge", "\u{2265}"), (b"gel", "\u{22DB}"), (b"geq", "\u{2265}"), (b"geqq", "\u{2267}"), (b"geqslant", "\u{2A7E}"), (b"ges", "\u{2A7E}"), (b"gescc", "\u{2AA9}"), (b"gesdot", "\u{2A80}"), (b"gesdoto", "\u{2A82}"), (b"gesdotol", "\u{2A84}"), (b"gesl", "\u{22DB}\u{FE00}"), (b"gesles", "\u{2A94}"), (b"gfr", "\u{1D524}"), (b"gg", "\u{226B}"), (b"ggg", "\u{22D9}"), (b"gimel", "\u{2137}"), (b"gjcy", "\u{0453}"), (b"gl", "\u{2277}"), (b"glE", "\u{2A92}"), (b"gla", "\u{2AA5}"), (b"glj", "\u{2AA4}"), (b"gnE", "\u{2269}"), (b"gnap", "\u{2A8A}"), (b"gnapprox", "\u{2A8A}"), (b"gne", "\u{2A88}"), (b"gneq", "\u{2A88}"), (b"gneqq", "\u{2269}"), (b"gnsim", "\u{22E7}"), (b"gopf", "\u{1D558}"), (b"grave", "\u{0060}"), (b"gscr", "\u{210A}"), (b"gsim", "\u{2273}"), (b"gsime", "\u{2A8E}"), (b"gsiml", "\u{2A90}"), (b"gt", "\u{003E}"), (b"gtcc", "\u{2AA7}"), (b"gtcir", "\u{2A7A}"), (b"gtdot", "\u{22D7}"), (b"gtlPar", "\u{2995}"), (b"gtquest", "\u{2A7C}"), (b"gtrapprox", "\u{2A86}"), (b"gtrarr", "\u{2978}"), (b"gtrdot", "\u{22D7}"), (b"gtreqless", "\u{22DB}"), (b"gtreqqless", "\u{2A8C}"), (b"gtrless", "\u{2277}"), (b"gtrsim", "\u{2273}"), (b"gvertneqq", "\u{2269}\u{FE00}"), (b"gvnE", "\u{2269}\u{FE00}"), (b"hArr", "\u{21D4}"), (b"hairsp", "\u{200A}"), (b"half", "\u{00BD}"), (b"hamilt", "\u{210B}"), (b"hardcy", "\u{044A}"), (b"harr", "\u{2194}"), (b"harrcir", "\u{2948}"), (b"harrw", "\u{21AD}"), (b"hbar", "\u{210F}"), (b"hcirc", "\u{0125}"), (b"hearts", "\u{2665}"), (b"heartsuit", "\u{2665}"), (b"hellip", "\u{2026}"), (b"hercon", "\u{22B9}"), (b"hfr", "\u{1D525}"), (b"hksearow", "\u{2925}"), (b"hkswarow", "\u{2926}"), (b"hoarr", "\u{21FF}"), (b"homtht", "\u{223B}"), (b"hookleftarrow", "\u{21A9}"), (b"hookrightarrow", "\u{21AA}"), (b"hopf", "\u{1D559}"), (b"horbar", "\u{2015}"), (b"hscr", "\u{1D4BD}"), (b"hslash", "\u{210F}"), (b"hstrok", "\u{0127}"), (b"hybull", "\u{2043}"), (b"hyphen", "\u{2010}"), (b"iacute", "\u{00ED}"), (b"ic", "\u{2063}"), (b"icirc", "\u{00EE}"), (b"icy", "\u{0438}"), (b"iecy", "\u{0435}"), (b"iexcl", "\u{00A1}"), (b"iff", "\u{21D4}"), (b"ifr", "\u{1D526}"), (b"igrave", "\u{00EC}"), (b"ii", "\u{2148}"), (b"iiiint", "\u{2A0C}"), (b"iiint", "\u{222D}"), (b"iinfin", "\u{29DC}"), (b"iiota", "\u{2129}"), (b"ijlig", "\u{0133}"), (b"imacr", "\u{012B}"), (b"image", "\u{2111}"), (b"imagline", "\u{2110}"), (b"imagpart", "\u{2111}"), (b"imath", "\u{0131}"), (b"imof", "\u{22B7}"), (b"imped", "\u{01B5}"), (b"in", "\u{2208}"), (b"incare", "\u{2105}"), (b"infin", "\u{221E}"), (b"infintie", "\u{29DD}"), (b"inodot", "\u{0131}"), (b"int", "\u{222B}"), (b"intcal", "\u{22BA}"), (b"integers", "\u{2124}"), (b"intercal", "\u{22BA}"), (b"intlarhk", "\u{2A17}"), (b"intprod", "\u{2A3C}"), (b"iocy", "\u{0451}"), (b"iogon", "\u{012F}"), (b"iopf", "\u{1D55A}"), (b"iota", "\u{03B9}"), (b"iprod", "\u{2A3C}"), (b"iquest", "\u{00BF}"), (b"iscr", "\u{1D4BE}"), (b"isin", "\u{2208}"), (b"isinE", "\u{22F9}"), (b"isindot", "\u{22F5}"), (b"isins", "\u{22F4}"), (b"isinsv", "\u{22F3}"), (b"isinv", "\u{2208}"), (b"it", "\u{2062}"), (b"itilde", "\u{0129}"), (b"iukcy", "\u{0456}"), (b"iuml", "\u{00EF}"), (b"jcirc", "\u{0135}"), (b"jcy", "\u{0439}"), (b"jfr", "\u{1D527}"), (b"jmath", "\u{0237}"), (b"jopf", "\u{1D55B}"), (b"jscr", "\u{1D4BF}"), (b"jsercy", "\u{0458}"), (b"jukcy", "\u{0454}"), (b"kappa", "\u{03BA}"), (b"kappav", "\u{03F0}"), (b"kcedil", "\u{0137}"), (b"kcy", "\u{043A}"), (b"kfr", "\u{1D528}"), (b"kgreen", "\u{0138}"), (b"khcy", "\u{0445}"), (b"kjcy", "\u{045C}"), (b"kopf", "\u{1D55C}"), (b"kscr", "\u{1D4C0}"), (b"lAarr", "\u{21DA}"), (b"lArr", "\u{21D0}"), (b"lAtail", "\u{291B}"), (b"lBarr", "\u{290E}"), (b"lE", "\u{2266}"), (b"lEg", "\u{2A8B}"), (b"lHar", "\u{2962}"), (b"lacute", "\u{013A}"), (b"laemptyv", "\u{29B4}"), (b"lagran", "\u{2112}"), (b"lambda", "\u{03BB}"), (b"lang", "\u{27E8}"), (b"langd", "\u{2991}"), (b"langle", "\u{27E8}"), (b"lap", "\u{2A85}"), (b"laquo", "\u{00AB}"), (b"larr", "\u{2190}"), (b"larrb", "\u{21E4}"), (b"larrbfs", "\u{291F}"), (b"larrfs", "\u{291D}"), (b"larrhk", "\u{21A9}"), (b"larrlp", "\u{21AB}"), (b"larrpl", "\u{2939}"), (b"larrsim", "\u{2973}"), (b"larrtl", "\u{21A2}"), (b"lat", "\u{2AAB}"), (b"latail", "\u{2919}"), (b"late", "\u{2AAD}"), (b"lates", "\u{2AAD}\u{FE00}"), (b"lbarr", "\u{290C}"), (b"lbbrk", "\u{2772}"), (b"lbrace", "\u{007B}"), (b"lbrack", "\u{005B}"), (b"lbrke", "\u{298B}"), (b"lbrksld", "\u{298F}"), (b"lbrkslu", "\u{298D}"), (b"lcaron", "\u{013E}"), (b"lcedil", "\u{013C}"), (b"lceil", "\u{2308}"), (b"lcub", "\u{007B}"), (b"lcy", "\u{043B}"), (b"ldca", "\u{2936}"), (b"ldquo", "\u{201C}"), (b"ldquor", "\u{201E}"), (b"ldrdhar", "\u{2967}"), (b"ldrushar", "\u{294B}"), (b"ldsh", "\u{21B2}"), (b"le", "\u{2264}"), (b"leftarrow", "\u{2190}"), (b"leftarrowtail", "\u{21A2}"), (b"leftharpoondown", "\u{21BD}"), (b"leftharpoonup", "\u{21BC}"), (b"leftleftarrows", "\u{21C7}"), (b"leftrightarrow", "\u{2194}"), (b"leftrightarrows", "\u{21C6}"), (b"leftrightharpoons", "\u{21CB}"), (b"leftrightsquigarrow", "\u{21AD}"), (b"leftthreetimes", "\u{22CB}"), (b"leg", "\u{22DA}"), (b"leq", "\u{2264}"), (b"leqq", "\u{2266}"), (b"leqslant", "\u{2A7D}"), (b"les", "\u{2A7D}"), (b"lescc", "\u{2AA8}"), (b"lesdot", "\u{2A7F}"), (b"lesdoto", "\u{2A81}"), (b"lesdotor", "\u{2A83}"), (b"lesg", "\u{22DA}\u{FE00}"), (b"lesges", "\u{2A93}"), (b"lessapprox", "\u{2A85}"), (b"lessdot", "\u{22D6}"), (b"lesseqgtr", "\u{22DA}"), (b"lesseqqgtr", "\u{2A8B}"), (b"lessgtr", "\u{2276}"), (b"lesssim", "\u{2272}"), (b"lfisht", "\u{297C}"), (b"lfloor", "\u{230A}"), (b"lfr", "\u{1D529}"), (b"lg", "\u{2276}"), (b"lgE", "\u{2A91}"), (b"lhard", "\u{21BD}"), (b"lharu", "\u{21BC}"), (b"lharul", "\u{296A}"), (b"lhblk", "\u{2584}"), (b"ljcy", "\u{0459}"), (b"ll", "\u{226A}"), (b"llarr", "\u{21C7}"), (b"llcorner", "\u{231E}"), (b"llhard", "\u{296B}"), (b"lltri", "\u{25FA}"), (b"lmidot", "\u{0140}"), (b"lmoust", "\u{23B0}"), (b"lmoustache", "\u{23B0}"), (b"lnE", "\u{2268}"), (b"lnap", "\u{2A89}"), (b"lnapprox", "\u{2A89}"), (b"lne", "\u{2A87}"), (b"lneq", "\u{2A87}"), (b"lneqq", "\u{2268}"), (b"lnsim", "\u{22E6}"), (b"loang", "\u{27EC}"), (b"loarr", "\u{21FD}"), (b"lobrk", "\u{27E6}"), (b"longleftarrow", "\u{27F5}"), (b"longleftrightarrow", "\u{27F7}"), (b"longmapsto", "\u{27FC}"), (b"longrightarrow", "\u{27F6}"), (b"looparrowleft", "\u{21AB}"), (b"looparrowright", "\u{21AC}"), (b"lopar", "\u{2985}"), (b"lopf", "\u{1D55D}"), (b"loplus", "\u{2A2D}"), (b"lotimes", "\u{2A34}"), (b"lowast", "\u{2217}"), (b"lowbar", "\u{005F}"), (b"loz", "\u{25CA}"), (b"lozenge", "\u{25CA}"), (b"lozf", "\u{29EB}"), (b"lpar", "\u{0028}"), (b"lparlt", "\u{2993}"), (b"lrarr", "\u{21C6}"), (b"lrcorner", "\u{231F}"), (b"lrhar", "\u{21CB}"), (b"lrhard", "\u{296D}"), (b"lrm", "\u{200E}"), (b"lrtri", "\u{22BF}"), (b"lsaquo", "\u{2039}"), (b"lscr", "\u{1D4C1}"), (b"lsh", "\u{21B0}"), (b"lsim", "\u{2272}"), (b"lsime", "\u{2A8D}"), (b"lsimg", "\u{2A8F}"), (b"lsqb", "\u{005B}"), (b"lsquo", "\u{2018}"), (b"lsquor", "\u{201A}"), (b"lstrok", "\u{0142}"), (b"lt", "\u{003C}"), (b"ltcc", "\u{2AA6}"), (b"ltcir", "\u{2A79}"), (b"ltdot", "\u{22D6}"), (b"lthree", "\u{22CB}"), (b"ltimes", "\u{22C9}"), (b"ltlarr", "\u{2976}"), (b"ltquest", "\u{2A7B}"), (b"ltrPar", "\u{2996}"), (b"ltri", "\u{25C3}"), (b"ltrie", "\u{22B4}"), (b"ltrif", "\u{25C2}"), (b"lurdshar", "\u{294A}"), (b"luruhar", "\u{2966}"), (b"lvertneqq", "\u{2268}\u{FE00}"), (b"lvnE", "\u{2268}\u{FE00}"), (b"mDDot", "\u{223A}"), (b"macr", "\u{00AF}"), (b"male", "\u{2642}"), (b"malt", "\u{2720}"), (b"maltese", "\u{2720}"), (b"map", "\u{21A6}"), (b"mapsto", "\u{21A6}"), (b"mapstodown", "\u{21A7}"), (b"mapstoleft", "\u{21A4}"), (b"mapstoup", "\u{21A5}"), (b"marker", "\u{25AE}"), (b"mcomma", "\u{2A29}"), (b"mcy", "\u{043C}"), (b"mdash", "\u{2014}"), (b"measuredangle", "\u{2221}"), (b"mfr", "\u{1D52A}"), (b"mho", "\u{2127}"), (b"micro", "\u{00B5}"), (b"mid", "\u{2223}"), (b"midast", "\u{002A}"), (b"midcir", "\u{2AF0}"), (b"middot", "\u{00B7}"), (b"minus", "\u{2212}"), (b"minusb", "\u{229F}"), (b"minusd", "\u{2238}"), (b"minusdu", "\u{2A2A}"), (b"mlcp", "\u{2ADB}"), (b"mldr", "\u{2026}"), (b"mnplus", "\u{2213}"), (b"models", "\u{22A7}"), (b"mopf", "\u{1D55E}"), (b"mp", "\u{2213}"), (b"mscr", "\u{1D4C2}"), (b"mstpos", "\u{223E}"), (b"mu", "\u{03BC}"), (b"multimap", "\u{22B8}"), (b"mumap", "\u{22B8}"), (b"nGg", "\u{22D9}\u{0338}"), (b"nGt", "\u{226B}\u{20D2}"), (b"nGtv", "\u{226B}\u{0338}"), (b"nLeftarrow", "\u{21CD}"), (b"nLeftrightarrow", "\u{21CE}"), (b"nLl", "\u{22D8}\u{0338}"), (b"nLt", "\u{226A}\u{20D2}"), (b"nLtv", "\u{226A}\u{0338}"), (b"nRightarrow", "\u{21CF}"), (b"nVDash", "\u{22AF}"), (b"nVdash", "\u{22AE}"), (b"nabla", "\u{2207}"), (b"nacute", "\u{0144}"), (b"nang", "\u{2220}\u{20D2}"), (b"nap", "\u{2249}"), (b"napE", "\u{2A70}\u{0338}"), (b"napid", "\u{224B}\u{0338}"), (b"napos", "\u{0149}"), (b"napprox", "\u{2249}"), (b"natur", "\u{266E}"), (b"natural", "\u{266E}"), (b"naturals", "\u{2115}"), (b"nbsp", "\u{00A0}"), (b"nbump", "\u{224E}\u{0338}"), (b"nbumpe", "\u{224F}\u{0338}"), (b"ncap", "\u{2A43}"), (b"ncaron", "\u{0148}"), (b"ncedil", "\u{0146}"), (b"ncong", "\u{2247}"), (b"ncongdot", "\u{2A6D}\u{0338}"), (b"ncup", "\u{2A42}"), (b"ncy", "\u{043D}"), (b"ndash", "\u{2013}"), (b"ne", "\u{2260}"), (b"neArr", "\u{21D7}"), (b"nearhk", "\u{2924}"), (b"nearr", "\u{2197}"), (b"nearrow", "\u{2197}"), (b"nedot", "\u{2250}\u{0338}"), (b"nequiv", "\u{2262}"), (b"nesear", "\u{2928}"), (b"nesim", "\u{2242}\u{0338}"), (b"nexist", "\u{2204}"), (b"nexists", "\u{2204}"), (b"nfr", "\u{1D52B}"), (b"ngE", "\u{2267}\u{0338}"), (b"nge", "\u{2271}"), (b"ngeq", "\u{2271}"), (b"ngeqq", "\u{2267}\u{0338}"), (b"ngeqslant", "\u{2A7E}\u{0338}"), (b"nges", "\u{2A7E}\u{0338}"), (b"ngsim", "\u{2275}"), (b"ngt", "\u{226F}"), (b"ngtr", "\u{226F}"), (b"nhArr", "\u{21CE}"), (b"nharr", "\u{21AE}"), (b"nhpar", "\u{2AF2}"), (b"ni", "\u{220B}"), (b"nis", "\u{22FC}"), (b"nisd", "\u{22FA}"), (b"niv", "\u{220B}"), (b"njcy", "\u{045A}"), (b"nlArr", "\u{21CD}"), (b"nlE", "\u{2266}\u{0338}"), (b"nlarr", "\u{219A}"), (b"nldr", "\u{2025}"), (b"nle", "\u{2270}"), (b"nleftarrow", "\u{219A}"), (b"nleftrightarrow", "\u{21AE}"), (b"nleq", "\u{2270}"), (b"nleqq", "\u{2266}\u{0338}"), (b"nleqslant", "\u{2A7D}\u{0338}"), (b"nles", "\u{2A7D}\u{0338}"), (b"nless", "\u{226E}"), (b"nlsim", "\u{2274}"), (b"nlt", "\u{226E}"), (b"nltri", "\u{22EA}"), (b"nltrie", "\u{22EC}"), (b"nmid", "\u{2224}"), (b"nopf", "\u{1D55F}"), (b"not", "\u{00AC}"), (b"notin", "\u{2209}"), (b"notinE", "\u{22F9}\u{0338}"), (b"notindot", "\u{22F5}\u{0338}"), (b"notinva", "\u{2209}"), (b"notinvb", "\u{22F7}"), (b"notinvc", "\u{22F6}"), (b"notni", "\u{220C}"), (b"notniva", "\u{220C}"), (b"notnivb", "\u{22FE}"), (b"notnivc", "\u{22FD}"), (b"npar", "\u{2226}"), (b"nparallel", "\u{2226}"), (b"nparsl", "\u{2AFD}\u{20E5}"), (b"npart", "\u{2202}\u{0338}"), (b"npolint", "\u{2A14}"), (b"npr", "\u{2280}"), (b"nprcue", "\u{22E0}"), (b"npre", "\u{2AAF}\u{0338}"), (b"nprec", "\u{2280}"), (b"npreceq", "\u{2AAF}\u{0338}"), (b"nrArr", "\u{21CF}"), (b"nrarr", "\u{219B}"), (b"nrarrc", "\u{2933}\u{0338}"), (b"nrarrw", "\u{219D}\u{0338}"), (b"nrightarrow", "\u{219B}"), (b"nrtri", "\u{22EB}"), (b"nrtrie", "\u{22ED}"), (b"nsc", "\u{2281}"), (b"nsccue", "\u{22E1}"), (b"nsce", "\u{2AB0}\u{0338}"), (b"nscr", "\u{1D4C3}"), (b"nshortmid", "\u{2224}"), (b"nshortparallel", "\u{2226}"), (b"nsim", "\u{2241}"), (b"nsime", "\u{2244}"), (b"nsimeq", "\u{2244}"), (b"nsmid", "\u{2224}"), (b"nspar", "\u{2226}"), (b"nsqsube", "\u{22E2}"), (b"nsqsupe", "\u{22E3}"), (b"nsub", "\u{2284}"), (b"nsubE", "\u{2AC5}\u{0338}"), (b"nsube", "\u{2288}"), (b"nsubset", "\u{2282}\u{20D2}"), (b"nsubseteq", "\u{2288}"), (b"nsubseteqq", "\u{2AC5}\u{0338}"), (b"nsucc", "\u{2281}"), (b"nsucceq", "\u{2AB0}\u{0338}"), (b"nsup", "\u{2285}"), (b"nsupE", "\u{2AC6}\u{0338}"), (b"nsupe", "\u{2289}"), (b"nsupset", "\u{2283}\u{20D2}"), (b"nsupseteq", "\u{2289}"), (b"nsupseteqq", "\u{2AC6}\u{0338}"), (b"ntgl", "\u{2279}"), (b"ntilde", "\u{00F1}"), (b"ntlg", "\u{2278}"), (b"ntriangleleft", "\u{22EA}"), (b"ntrianglelefteq", "\u{22EC}"), (b"ntriangleright", "\u{22EB}"), (b"ntrianglerighteq", "\u{22ED}"), (b"nu", "\u{03BD}"), (b"num", "\u{0023}"), (b"numero", "\u{2116}"), (b"numsp", "\u{2007}"), (b"nvDash", "\u{22AD}"), (b"nvHarr", "\u{2904}"), (b"nvap", "\u{224D}\u{20D2}"), (b"nvdash", "\u{22AC}"), (b"nvge", "\u{2265}\u{20D2}"), (b"nvgt", "\u{003E}\u{20D2}"), (b"nvinfin", "\u{29DE}"), (b"nvlArr", "\u{2902}"), (b"nvle", "\u{2264}\u{20D2}"), (b"nvlt", "\u{003C}\u{20D2}"), (b"nvltrie", "\u{22B4}\u{20D2}"), (b"nvrArr", "\u{2903}"), (b"nvrtrie", "\u{22B5}\u{20D2}"), (b"nvsim", "\u{223C}\u{20D2}"), (b"nwArr", "\u{21D6}"), (b"nwarhk", "\u{2923}"), (b"nwarr", "\u{2196}"), (b"nwarrow", "\u{2196}"), (b"nwnear", "\u{2927}"), (b"oS", "\u{24C8}"), (b"oacute", "\u{00F3}"), (b"oast", "\u{229B}"), (b"ocir", "\u{229A}"), (b"ocirc", "\u{00F4}"), (b"ocy", "\u{043E}"), (b"odash", "\u{229D}"), (b"odblac", "\u{0151}"), (b"odiv", "\u{2A38}"), (b"odot", "\u{2299}"), (b"odsold", "\u{29BC}"), (b"oelig", "\u{0153}"), (b"ofcir", "\u{29BF}"), (b"ofr", "\u{1D52C}"), (b"ogon", "\u{02DB}"), (b"ograve", "\u{00F2}"), (b"ogt", "\u{29C1}"), (b"ohbar", "\u{29B5}"), (b"ohm", "\u{03A9}"), (b"oint", "\u{222E}"), (b"olarr", "\u{21BA}"), (b"olcir", "\u{29BE}"), (b"olcross", "\u{29BB}"), (b"oline", "\u{203E}"), (b"olt", "\u{29C0}"), (b"omacr", "\u{014D}"), (b"omega", "\u{03C9}"), (b"omicron", "\u{03BF}"), (b"omid", "\u{29B6}"), (b"ominus", "\u{2296}"), (b"oopf", "\u{1D560}"), (b"opar", "\u{29B7}"), (b"operp", "\u{29B9}"), (b"oplus", "\u{2295}"), (b"or", "\u{2228}"), (b"orarr", "\u{21BB}"), (b"ord", "\u{2A5D}"), (b"order", "\u{2134}"), (b"orderof", "\u{2134}"), (b"ordf", "\u{00AA}"), (b"ordm", "\u{00BA}"), (b"origof", "\u{22B6}"), (b"oror", "\u{2A56}"), (b"orslope", "\u{2A57}"), (b"orv", "\u{2A5B}"), (b"oscr", "\u{2134}"), (b"oslash", "\u{00F8}"), (b"osol", "\u{2298}"), (b"otilde", "\u{00F5}"), (b"otimes", "\u{2297}"), (b"otimesas", "\u{2A36}"), (b"ouml", "\u{00F6}"), (b"ovbar", "\u{233D}"), (b"par", "\u{2225}"), (b"para", "\u{00B6}"), (b"parallel", "\u{2225}"), (b"parsim", "\u{2AF3}"), (b"parsl", "\u{2AFD}"), (b"part", "\u{2202}"), (b"pcy", "\u{043F}"), (b"percnt", "\u{0025}"), (b"period", "\u{002E}"), (b"permil", "\u{2030}"), (b"perp", "\u{22A5}"), (b"pertenk", "\u{2031}"), (b"pfr", "\u{1D52D}"), (b"phi", "\u{03C6}"), (b"phiv", "\u{03D5}"), (b"phmmat", "\u{2133}"), (b"phone", "\u{260E}"), (b"pi", "\u{03C0}"), (b"pitchfork", "\u{22D4}"), (b"piv", "\u{03D6}"), (b"planck", "\u{210F}"), (b"planckh", "\u{210E}"), (b"plankv", "\u{210F}"), (b"plus", "\u{002B}"), (b"plusacir", "\u{2A23}"), (b"plusb", "\u{229E}"), (b"pluscir", "\u{2A22}"), (b"plusdo", "\u{2214}"), (b"plusdu", "\u{2A25}"), (b"pluse", "\u{2A72}"), (b"plusmn", "\u{00B1}"), (b"plussim", "\u{2A26}"), (b"plustwo", "\u{2A27}"), (b"pm", "\u{00B1}"), (b"pointint", "\u{2A15}"), (b"popf", "\u{1D561}"), (b"pound", "\u{00A3}"), (b"pr", "\u{227A}"), (b"prE", "\u{2AB3}"), (b"prap", "\u{2AB7}"), (b"prcue", "\u{227C}"), (b"pre", "\u{2AAF}"), (b"prec", "\u{227A}"), (b"precapprox", "\u{2AB7}"), (b"preccurlyeq", "\u{227C}"), (b"preceq", "\u{2AAF}"), (b"precnapprox", "\u{2AB9}"), (b"precneqq", "\u{2AB5}"), (b"precnsim", "\u{22E8}"), (b"precsim", "\u{227E}"), (b"prime", "\u{2032}"), (b"primes", "\u{2119}"), (b"prnE", "\u{2AB5}"), (b"prnap", "\u{2AB9}"), (b"prnsim", "\u{22E8}"), (b"prod", "\u{220F}"), (b"profalar", "\u{232E}"), (b"profline", "\u{2312}"), (b"profsurf", "\u{2313}"), (b"prop", "\u{221D}"), (b"propto", "\u{221D}"), (b"prsim", "\u{227E}"), (b"prurel", "\u{22B0}"), (b"pscr", "\u{1D4C5}"), (b"psi", "\u{03C8}"), (b"puncsp", "\u{2008}"), (b"qfr", "\u{1D52E}"), (b"qint", "\u{2A0C}"), (b"qopf", "\u{1D562}"), (b"qprime", "\u{2057}"), (b"qscr", "\u{1D4C6}"), (b"quaternions", "\u{210D}"), (b"quatint", "\u{2A16}"), (b"quest", "\u{003F}"), (b"questeq", "\u{225F}"), (b"quot", "\u{0022}"), (b"rAarr", "\u{21DB}"), (b"rArr", "\u{21D2}"), (b"rAtail", "\u{291C}"), (b"rBarr", "\u{290F}"), (b"rHar", "\u{2964}"), (b"race", "\u{223D}\u{0331}"), (b"racute", "\u{0155}"), (b"radic", "\u{221A}"), (b"raemptyv", "\u{29B3}"), (b"rang", "\u{27E9}"), (b"rangd", "\u{2992}"), (b"range", "\u{29A5}"), (b"rangle", "\u{27E9}"), (b"raquo", "\u{00BB}"), (b"rarr", "\u{2192}"), (b"rarrap", "\u{2975}"), (b"rarrb", "\u{21E5}"), (b"rarrbfs", "\u{2920}"), (b"rarrc", "\u{2933}"), (b"rarrfs", "\u{291E}"), (b"rarrhk", "\u{21AA}"), (b"rarrlp", "\u{21AC}"), (b"rarrpl", "\u{2945}"), (b"rarrsim", "\u{2974}"), (b"rarrtl", "\u{21A3}"), (b"rarrw", "\u{219D}"), (b"ratail", "\u{291A}"), (b"ratio", "\u{2236}"), (b"rationals", "\u{211A}"), (b"rbarr", "\u{290D}"), (b"rbbrk", "\u{2773}"), (b"rbrace", "\u{007D}"), (b"rbrack", "\u{005D}"), (b"rbrke", "\u{298C}"), (b"rbrksld", "\u{298E}"), (b"rbrkslu", "\u{2990}"), (b"rcaron", "\u{0159}"), (b"rcedil", "\u{0157}"), (b"rceil", "\u{2309}"), (b"rcub", "\u{007D}"), (b"rcy", "\u{0440}"), (b"rdca", "\u{2937}"), (b"rdldhar", "\u{2969}"), (b"rdquo", "\u{201D}"), (b"rdquor", "\u{201D}"), (b"rdsh", "\u{21B3}"), (b"real", "\u{211C}"), (b"realine", "\u{211B}"), (b"realpart", "\u{211C}"), (b"reals", "\u{211D}"), (b"rect", "\u{25AD}"), (b"reg", "\u{00AE}"), (b"rfisht", "\u{297D}"), (b"rfloor", "\u{230B}"), (b"rfr", "\u{1D52F}"), (b"rhard", "\u{21C1}"), (b"rharu", "\u{21C0}"), (b"rharul", "\u{296C}"), (b"rho", "\u{03C1}"), (b"rhov", "\u{03F1}"), (b"rightarrow", "\u{2192}"), (b"rightarrowtail", "\u{21A3}"), (b"rightharpoondown", "\u{21C1}"), (b"rightharpoonup", "\u{21C0}"), (b"rightleftarrows", "\u{21C4}"), (b"rightleftharpoons", "\u{21CC}"), (b"rightrightarrows", "\u{21C9}"), (b"rightsquigarrow", "\u{219D}"), (b"rightthreetimes", "\u{22CC}"), (b"ring", "\u{02DA}"), (b"risingdotseq", "\u{2253}"), (b"rlarr", "\u{21C4}"), (b"rlhar", "\u{21CC}"), (b"rlm", "\u{200F}"), (b"rmoust", "\u{23B1}"), (b"rmoustache", "\u{23B1}"), (b"rnmid", "\u{2AEE}"), (b"roang", "\u{27ED}"), (b"roarr", "\u{21FE}"), (b"robrk", "\u{27E7}"), (b"ropar", "\u{2986}"), (b"ropf", "\u{1D563}"), (b"roplus", "\u{2A2E}"), (b"rotimes", "\u{2A35}"), (b"rpar", "\u{0029}"), (b"rpargt", "\u{2994}"), (b"rppolint", "\u{2A12}"), (b"rrarr", "\u{21C9}"), (b"rsaquo", "\u{203A}"), (b"rscr", "\u{1D4C7}"), (b"rsh", "\u{21B1}"), (b"rsqb", "\u{005D}"), (b"rsquo", "\u{2019}"), (b"rsquor", "\u{2019}"), (b"rthree", "\u{22CC}"), (b"rtimes", "\u{22CA}"), (b"rtri", "\u{25B9}"), (b"rtrie", "\u{22B5}"), (b"rtrif", "\u{25B8}"), (b"rtriltri", "\u{29CE}"), (b"ruluhar", "\u{2968}"), (b"rx", "\u{211E}"), (b"sacute", "\u{015B}"), (b"sbquo", "\u{201A}"), (b"sc", "\u{227B}"), (b"scE", "\u{2AB4}"), (b"scap", "\u{2AB8}"), (b"scaron", "\u{0161}"), (b"sccue", "\u{227D}"), (b"sce", "\u{2AB0}"), (b"scedil", "\u{015F}"), (b"scirc", "\u{015D}"), (b"scnE", "\u{2AB6}"), (b"scnap", "\u{2ABA}"), (b"scnsim", "\u{22E9}"), (b"scpolint", "\u{2A13}"), (b"scsim", "\u{227F}"), (b"scy", "\u{0441}"), (b"sdot", "\u{22C5}"), (b"sdotb", "\u{22A1}"), (b"sdote", "\u{2A66}"), (b"seArr", "\u{21D8}"), (b"searhk", "\u{2925}"), (b"searr", "\u{2198}"), (b"searrow", "\u{2198}"), (b"sect", "\u{00A7}"), (b"semi", "\u{003B}"), (b"seswar", "\u{2929}"), (b"setminus", "\u{2216}"), (b"setmn", "\u{2216}"), (b"sext", "\u{2736}"), (b"sfr", "\u{1D530}"), (b"sfrown", "\u{2322}"), (b"sharp", "\u{266F}"), (b"shchcy", "\u{0449}"), (b"shcy", "\u{0448}"), (b"shortmid", "\u{2223}"), (b"shortparallel", "\u{2225}"), (b"shy", "\u{00AD}"), (b"sigma", "\u{03C3}"), (b"sigmaf", "\u{03C2}"), (b"sigmav", "\u{03C2}"), (b"sim", "\u{223C}"), (b"simdot", "\u{2A6A}"), (b"sime", "\u{2243}"), (b"simeq", "\u{2243}"), (b"simg", "\u{2A9E}"), (b"simgE", "\u{2AA0}"), (b"siml", "\u{2A9D}"), (b"simlE", "\u{2A9F}"), (b"simne", "\u{2246}"), (b"simplus", "\u{2A24}"), (b"simrarr", "\u{2972}"), (b"slarr", "\u{2190}"), (b"smallsetminus", "\u{2216}"), (b"smashp", "\u{2A33}"), (b"smeparsl", "\u{29E4}"), (b"smid", "\u{2223}"), (b"smile", "\u{2323}"), (b"smt", "\u{2AAA}"), (b"smte", "\u{2AAC}"), (b"smtes", "\u{2AAC}\u{FE00}"), (b"softcy", "\u{044C}"), (b"sol", "\u{002F}"), (b"solb", "\u{29C4}"), (b"solbar", "\u{233F}"), (b"sopf", "\u{1D564}"), (b"spades", "\u{2660}"), (b"spadesuit", "\u{2660}"), (b"spar", "\u{2225}"), (b"sqcap", "\u{2293}"), (b"sqcaps", "\u{2293}\u{FE00}"), (b"sqcup", "\u{2294}"), (b"sqcups", "\u{2294}\u{FE00}"), (b"sqsub", "\u{228F}"), (b"sqsube", "\u{2291}"), (b"sqsubset", "\u{228F}"), (b"sqsubseteq", "\u{2291}"), (b"sqsup", "\u{2290}"), (b"sqsupe", "\u{2292}"), (b"sqsupset", "\u{2290}"), (b"sqsupseteq", "\u{2292}"), (b"squ", "\u{25A1}"), (b"square", "\u{25A1}"), (b"squarf", "\u{25AA}"), (b"squf", "\u{25AA}"), (b"srarr", "\u{2192}"), (b"sscr", "\u{1D4C8}"), (b"ssetmn", "\u{2216}"), (b"ssmile", "\u{2323}"), (b"sstarf", "\u{22C6}"), (b"star", "\u{2606}"), (b"starf", "\u{2605}"), (b"straightepsilon", "\u{03F5}"), (b"straightphi", "\u{03D5}"), (b"strns", "\u{00AF}"), (b"sub", "\u{2282}"), (b"subE", "\u{2AC5}"), (b"subdot", "\u{2ABD}"), (b"sube", "\u{2286}"), (b"subedot", "\u{2AC3}"), (b"submult", "\u{2AC1}"), (b"subnE", "\u{2ACB}"), (b"subne", "\u{228A}"), (b"subplus", "\u{2ABF}"), (b"subrarr", "\u{2979}"), (b"subset", "\u{2282}"), (b"subseteq", "\u{2286}"), (b"subseteqq", "\u{2AC5}"), (b"subsetneq", "\u{228A}"), (b"subsetneqq", "\u{2ACB}"), (b"subsim", "\u{2AC7}"), (b"subsub", "\u{2AD5}"), (b"subsup", "\u{2AD3}"), (b"succ", "\u{227B}"), (b"succapprox", "\u{2AB8}"), (b"succcurlyeq", "\u{227D}"), (b"succeq", "\u{2AB0}"), (b"succnapprox", "\u{2ABA}"), (b"succneqq", "\u{2AB6}"), (b"succnsim", "\u{22E9}"), (b"succsim", "\u{227F}"), (b"sum", "\u{2211}"), (b"sung", "\u{266A}"), (b"sup", "\u{2283}"), (b"sup1", "\u{00B9}"), (b"sup2", "\u{00B2}"), (b"sup3", "\u{00B3}"), (b"supE", "\u{2AC6}"), (b"supdot", "\u{2ABE}"), (b"supdsub", "\u{2AD8}"), (b"supe", "\u{2287}"), (b"supedot", "\u{2AC4}"), (b"suphsol", "\u{27C9}"), (b"suphsub", "\u{2AD7}"), (b"suplarr", "\u{297B}"), (b"supmult", "\u{2AC2}"), (b"supnE", "\u{2ACC}"), (b"supne", "\u{228B}"), (b"supplus", "\u{2AC0}"), (b"supset", "\u{2283}"), (b"supseteq", "\u{2287}"), (b"supseteqq", "\u{2AC6}"), (b"supsetneq", "\u{228B}"), (b"supsetneqq", "\u{2ACC}"), (b"supsim", "\u{2AC8}"), (b"supsub", "\u{2AD4}"), (b"supsup", "\u{2AD6}"), (b"swArr", "\u{21D9}"), (b"swarhk", "\u{2926}"), (b"swarr", "\u{2199}"), (b"swarrow", "\u{2199}"), (b"swnwar", "\u{292A}"), (b"szlig", "\u{00DF}"), (b"target", "\u{2316}"), (b"tau", "\u{03C4}"), (b"tbrk", "\u{23B4}"), (b"tcaron", "\u{0165}"), (b"tcedil", "\u{0163}"), (b"tcy", "\u{0442}"), (b"tdot", "\u{20DB}"), (b"telrec", "\u{2315}"), (b"tfr", "\u{1D531}"), (b"there4", "\u{2234}"), (b"therefore", "\u{2234}"), (b"theta", "\u{03B8}"), (b"thetasym", "\u{03D1}"), (b"thetav", "\u{03D1}"), (b"thickapprox", "\u{2248}"), (b"thicksim", "\u{223C}"), (b"thinsp", "\u{2009}"), (b"thkap", "\u{2248}"), (b"thksim", "\u{223C}"), (b"thorn", "\u{00FE}"), (b"tilde", "\u{02DC}"), (b"times", "\u{00D7}"), (b"timesb", "\u{22A0}"), (b"timesbar", "\u{2A31}"), (b"timesd", "\u{2A30}"), (b"tint", "\u{222D}"), (b"toea", "\u{2928}"), (b"top", "\u{22A4}"), (b"topbot", "\u{2336}"), (b"topcir", "\u{2AF1}"), (b"topf", "\u{1D565}"), (b"topfork", "\u{2ADA}"), (b"tosa", "\u{2929}"), (b"tprime", "\u{2034}"), (b"trade", "\u{2122}"), (b"triangle", "\u{25B5}"), (b"triangledown", "\u{25BF}"), (b"triangleleft", "\u{25C3}"), (b"trianglelefteq", "\u{22B4}"), (b"triangleq", "\u{225C}"), (b"triangleright", "\u{25B9}"), (b"trianglerighteq", "\u{22B5}"), (b"tridot", "\u{25EC}"), (b"trie", "\u{225C}"), (b"triminus", "\u{2A3A}"), (b"triplus", "\u{2A39}"), (b"trisb", "\u{29CD}"), (b"tritime", "\u{2A3B}"), (b"trpezium", "\u{23E2}"), (b"tscr", "\u{1D4C9}"), (b"tscy", "\u{0446}"), (b"tshcy", "\u{045B}"), (b"tstrok", "\u{0167}"), (b"twixt", "\u{226C}"), (b"twoheadleftarrow", "\u{219E}"), (b"twoheadrightarrow", "\u{21A0}"), (b"uArr", "\u{21D1}"), (b"uHar", "\u{2963}"), (b"uacute", "\u{00FA}"), (b"uarr", "\u{2191}"), (b"ubrcy", "\u{045E}"), (b"ubreve", "\u{016D}"), (b"ucirc", "\u{00FB}"), (b"ucy", "\u{0443}"), (b"udarr", "\u{21C5}"), (b"udblac", "\u{0171}"), (b"udhar", "\u{296E}"), (b"ufisht", "\u{297E}"), (b"ufr", "\u{1D532}"), (b"ugrave", "\u{00F9}"), (b"uharl", "\u{21BF}"), (b"uharr", "\u{21BE}"), (b"uhblk", "\u{2580}"), (b"ulcorn", "\u{231C}"), (b"ulcorner", "\u{231C}"), (b"ulcrop", "\u{230F}"), (b"ultri", "\u{25F8}"), (b"umacr", "\u{016B}"), (b"uml", "\u{00A8}"), (b"uogon", "\u{0173}"), (b"uopf", "\u{1D566}"), (b"uparrow", "\u{2191}"), (b"updownarrow", "\u{2195}"), (b"upharpoonleft", "\u{21BF}"), (b"upharpoonright", "\u{21BE}"), (b"uplus", "\u{228E}"), (b"upsi", "\u{03C5}"), (b"upsih", "\u{03D2}"), (b"upsilon", "\u{03C5}"), (b"upuparrows", "\u{21C8}"), (b"urcorn", "\u{231D}"), (b"urcorner", "\u{231D}"), (b"urcrop", "\u{230E}"), (b"uring", "\u{016F}"), (b"urtri", "\u{25F9}"), (b"uscr", "\u{1D4CA}"), (b"utdot", "\u{22F0}"), (b"utilde", "\u{0169}"), (b"utri", "\u{25B5}"), (b"utrif", "\u{25B4}"), (b"uuarr", "\u{21C8}"), (b"uuml", "\u{00FC}"), (b"uwangle", "\u{29A7}"), (b"vArr", "\u{21D5}"), (b"vBar", "\u{2AE8}"), (b"vBarv", "\u{2AE9}"), (b"vDash", "\u{22A8}"), (b"vangrt", "\u{299C}"), (b"varepsilon", "\u{03F5}"), (b"varkappa", "\u{03F0}"), (b"varnothing", "\u{2205}"), (b"varphi", "\u{03D5}"), (b"varpi", "\u{03D6}"), (b"varpropto", "\u{221D}"), (b"varr", "\u{2195}"), (b"varrho", "\u{03F1}"), (b"varsigma", "\u{03C2}"), (b"varsubsetneq", "\u{228A}\u{FE00}"), (b"varsubsetneqq", "\u{2ACB}\u{FE00}"), (b"varsupsetneq", "\u{228B}\u{FE00}"), (b"varsupsetneqq", "\u{2ACC}\u{FE00}"), (b"vartheta", "\u{03D1}"), (b"vartriangleleft", "\u{22B2}"), (b"vartriangleright", "\u{22B3}"), (b"vcy", "\u{0432}"), (b"vdash", "\u{22A2}"), (b"vee", "\u{2228}"), (b"veebar", "\u{22BB}"), (b"veeeq", "\u{225A}"), (b"vellip", "\u{22EE}"), (b"verbar", "\u{007C}"), (b"vert", "\u{007C}"), (b"vfr", "\u{1D533}"), (b"vltri", "\u{22B2}"), (b"vnsub", "\u{2282}\u{20D2}"), (b"vnsup", "\u{2283}\u{20D2}"), (b"vopf", "\u{1D567}"), (b"vprop", "\u{221D}"), (b"vrtri", "\u{22B3}"), (b"vscr", "\u{1D4CB}"), (b"vsubnE", "\u{2ACB}\u{FE00}"), (b"vsubne", "\u{228A}\u{FE00}"), (b"vsupnE", "\u{2ACC}\u{FE00}"), (b"vsupne", "\u{228B}\u{FE00}"), (b"vzigzag", "\u{299A}"), (b"wcirc", "\u{0175}"), (b"wedbar", "\u{2A5F}"), (b"wedge", "\u{2227}"), (b"wedgeq", "\u{2259}"), (b"weierp", "\u{2118}"), (b"wfr", "\u{1D534}"), (b"wopf", "\u{1D568}"), (b"wp", "\u{2118}"), (b"wr", "\u{2240}"), (b"wreath", "\u{2240}"), (b"wscr", "\u{1D4CC}"), (b"xcap", "\u{22C2}"), (b"xcirc", "\u{25EF}"), (b"xcup", "\u{22C3}"), (b"xdtri", "\u{25BD}"), (b"xfr", "\u{1D535}"), (b"xhArr", "\u{27FA}"), (b"xharr", "\u{27F7}"), (b"xi", "\u{03BE}"), (b"xlArr", "\u{27F8}"), (b"xlarr", "\u{27F5}"), (b"xmap", "\u{27FC}"), (b"xnis", "\u{22FB}"), (b"xodot", "\u{2A00}"), (b"xopf", "\u{1D569}"), (b"xoplus", "\u{2A01}"), (b"xotime", "\u{2A02}"), (b"xrArr", "\u{27F9}"), (b"xrarr", "\u{27F6}"), (b"xscr", "\u{1D4CD}"), (b"xsqcup", "\u{2A06}"), (b"xuplus", "\u{2A04}"), (b"xutri", "\u{25B3}"), (b"xvee", "\u{22C1}"), (b"xwedge", "\u{22C0}"), (b"yacute", "\u{00FD}"), (b"yacy", "\u{044F}"), (b"ycirc", "\u{0177}"), (b"ycy", "\u{044B}"), (b"yen", "\u{00A5}"), (b"yfr", "\u{1D536}"), (b"yicy", "\u{0457}"), (b"yopf", "\u{1D56A}"), (b"yscr", "\u{1D4CE}"), (b"yucy", "\u{044E}"), (b"yuml", "\u{00FF}"), (b"zacute", "\u{017A}"), (b"zcaron", "\u{017E}"), (b"zcy", "\u{0437}"), (b"zdot", "\u{017C}"), (b"zeetrf", "\u{2128}"), (b"zeta", "\u{03B6}"), (b"zfr", "\u{1D537}"), (b"zhcy", "\u{0436}"), (b"zigrarr", "\u{21DD}"), (b"zopf", "\u{1D56B}"), (b"zscr", "\u{1D4CF}"), (b"zwj", "\u{200D}"), (b"zwnj", "\u{200C}"), ]; pub(crate) fn get_entity(bytes: &[u8]) -> Option<&'static str> { ENTITIES .binary_search_by_key(&bytes, |&(key, _value)| key) .ok() .map(|i| ENTITIES[i].1) } pulldown-cmark-0.10.3/src/firstpass.rs000064400000000000000000002742541046102023000160350ustar 00000000000000//! 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 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, }; use crate::{scanners::*, MetadataBlockKind}; use unicase::UniCase; /// Runs the first pass, which resolves the block structure of the document, /// and returns the resulting tree. pub(crate) fn run_first_pass(text: &str, options: Options) -> (Tree, Allocations<'_>) { // This is a very naive heuristic for the number of nodes // we'll need. let start_capacity = max(128, text.len() / 32); let lookup_table = &create_lut(&options); let first_pass = FirstPass { text, tree: Tree::with_capacity(start_capacity), begin_list_item: None, last_line_blank: false, allocs: Allocations::new(), options, lookup_table, next_paragraph_task: None, }; first_pass.run() } /// State for the first parsing pass. struct FirstPass<'a, 'b> { text: &'a str, tree: Tree, begin_list_item: Option, last_line_blank: bool, 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, } impl<'a, 'b> FirstPass<'a, 'b> { fn run(mut self) -> (Tree, Allocations<'a>) { let mut ix = 0; while ix < self.text.len() { ix = self.parse_block(ix); } for _ in 0..self.tree.spine_len() { self.pop(ix); } (self.tree, self.allocs) } /// Returns offset after block. fn parse_block(&mut self, mut start_ix: usize) -> usize { let bytes = self.text.as_bytes(); let mut line_start = LineStart::new(&bytes[start_ix..]); let i = scan_containers( &self.tree, &mut line_start, self.options.has_gfm_footnotes(), ); for _ in i..self.tree.spine_len() { self.pop(start_ix); } if self.options.contains(Options::ENABLE_OLD_FOOTNOTES) { // finish footnote if it's still open and was preceded by blank line if let Some(node_ix) = self.tree.peek_up() { if let ItemBody::FootnoteDefinition(..) = self.tree[node_ix].item.body { if self.last_line_blank { self.pop(start_ix); } } } // 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; } } else { line_start = save; } } let container_start = start_ix + line_start.bytes_scanned(); if let Some((ch, index, indent)) = line_start.scan_list_marker() { let after_marker_index = start_ix + line_start.bytes_scanned(); self.continue_list(container_start, ch, index); self.tree.append(Item { start: container_start, end: after_marker_index, // will get updated later if item not empty body: ItemBody::ListItem(indent), }); 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; } if self.options.contains(Options::ENABLE_TASKLISTS) { self.next_paragraph_task = line_start.scan_task_list_marker().map(|is_checked| Item { start: after_marker_index, end: start_ix + line_start.bytes_scanned(), body: ItemBody::TaskListMarker(is_checked), }); } } else if line_start.scan_blockquote_marker() { self.finish_list(start_ix); self.tree.append(Item { start: container_start, end: 0, // will get set later body: ItemBody::BlockQuote, }); self.tree.push(); } else { break; } } 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::BlockQuote => (), ItemBody::ListItem(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 // there are between this one and the next one, it cannot nest anything // else, so its indentation should not be subtracted from the line start. *indent = 0; } _ => { 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 { let ix = start_ix + line_start.bytes_scanned(); let remaining_space = line_start.remaining_space(); return self.parse_indented_code_block(ix, remaining_space); } let ix = start_ix + line_start.bytes_scanned(); if let Some((_n, metadata_block_ch)) = scan_metadata_block( &bytes[ix..], self.options .contains(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS), self.options .contains(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS), ) { return self.parse_metadata_block(ix, indent, metadata_block_ch); } // HTML Blocks if bytes[ix] == 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)..]) { return self.parse_html_block_type_1_to_5( ix, html_end_tag, remaining_space, indent, ); } // Detect type 6 if starts_html_block_type_6(&bytes[(ix + 1)..]) { 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..]) { return self.parse_html_block_type_6_or_7(ix, remaining_space, indent); } } if let Ok(n) = scan_hrule(&bytes[ix..]) { return self.parse_hrule(n, ix); } if let Some(atx_size) = scan_atx_heading(&bytes[ix..]) { return self.parse_atx_heading(ix, atx_size); } if let Some((n, fence_ch)) = scan_code_fence(&bytes[ix..]) { return self.parse_fenced_code_block(ix, indent, fence_ch, n); } // parse refdef while let Some((bytecount, label, link_def)) = self.parse_refdef_total(start_ix + line_start.bytes_scanned()) { self.allocs.refdefs.0.entry(label).or_insert(link_def); let container_start = start_ix + line_start.bytes_scanned(); let mut ix = container_start + bytecount; // Refdefs act as if they were contained within a paragraph, for purposes of lazy // continuations. For example: // // ``` // > [foo]: http://example.com // bar // ``` // // is equivalent to // // ``` // > bar // // [foo]: http://example.com // ``` 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 { return ix; } } let ix = start_ix + line_start.bytes_scanned(); self.parse_paragraph(ix) } /// Returns the offset of the first line after the table. /// Assumptions: current focus is a table element and the table header /// matches the separator line (same number of columns). fn parse_table( &mut self, table_cols: usize, head_start: usize, body_start: usize, ) -> Option { // filled empty cells are limited to protect against quadratic growth // https://github.com/raphlinus/pulldown-cmark/issues/832 let mut missing_empty_cells = 0; // parse header. this shouldn't fail because we made sure the table header is ok let (_sep_start, thead_ix) = self.parse_table_row_inner( head_start, table_cols, &mut missing_empty_cells, )?; self.tree[thead_ix].item.body = ItemBody::TableHead; // parse body let mut ix = body_start; while let Some((next_ix, _row_ix)) = self.parse_table_row( ix, table_cols, &mut missing_empty_cells, ) { ix = next_ix; } self.pop(ix); Some(ix) } /// Call this when containers are taken care of. /// Returns bytes scanned, row_ix fn parse_table_row_inner( &mut self, mut ix: usize, row_cells: usize, missing_empty_cells: &mut usize, ) -> Option<(usize, TreeIndex)> { // Limit to prevent a malicious input from causing a denial of service. const MAX_AUTOCOMPLETED_CELLS: usize = 1 << 18; // = 0x40000 let bytes = self.text.as_bytes(); let mut cells = 0; let mut final_cell_ix = None; let old_cur = self.tree.cur(); let row_ix = self.tree.append(Item { start: ix, end: 0, // set at end of this function body: ItemBody::TableRow, }); self.tree.push(); loop { ix += scan_ch(&bytes[ix..], b'|'); let start_ix = ix; ix += scan_whitespace_no_nl(&bytes[ix..]); if let Some(eol_bytes) = scan_eol(&bytes[ix..]) { ix += eol_bytes; break; } let cell_ix = self.tree.append(Item { start: start_ix, end: ix, body: ItemBody::TableCell, }); self.tree.push(); let (next_ix, _brk) = self.parse_line(ix, None, TableParseMode::Active); self.tree[cell_ix].item.end = next_ix; self.tree.pop(); ix = next_ix; cells += 1; if cells == row_cells { final_cell_ix = Some(cell_ix); } } if let (Some(cur), 0) = (old_cur, cells) { self.pop(ix); self.tree[cur].next = None; return None; } // fill empty cells if needed // note: this is where GFM and commonmark-extra diverge. we follow // GFM here for _ in cells..row_cells { if *missing_empty_cells >= MAX_AUTOCOMPLETED_CELLS { return None; } *missing_empty_cells += 1; self.tree.append(Item { start: ix, end: ix, body: ItemBody::TableCell, }); } // drop excess cells if let Some(cell_ix) = final_cell_ix { self.tree[cell_ix].next = None; } self.pop(ix); Some((ix, row_ix)) } /// Returns first offset after the row and the tree index of the row. fn parse_table_row( &mut self, mut ix: usize, row_cells: usize, missing_empty_cells: &mut usize, ) -> 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(); if !current_container { return None; } line_start.scan_all_space(); ix += line_start.bytes_scanned(); if scan_paragraph_interrupt_no_table( &bytes[ix..], current_container, self.options.has_gfm_footnotes(), &self.tree, ) { return None; } let (ix, row_ix) = self.parse_table_row_inner(ix, row_cells, missing_empty_cells)?; Some((ix, row_ix)) } /// Returns offset of line start after paragraph. fn parse_paragraph(&mut self, start_ix: usize) -> usize { let node_ix = self.tree.append(Item { start: start_ix, end: 0, // will get set later body: ItemBody::Paragraph, }); self.tree.push(); if let Some(item) = self.next_paragraph_task { self.tree.append(item); self.next_paragraph_task = None; } let bytes = self.text.as_bytes(); let mut ix = start_ix; loop { let scan_mode = if self.options.contains(Options::ENABLE_TABLES) && ix == start_ix { TableParseMode::Scan } else { TableParseMode::Disabled }; let (next_ix, brk) = self.parse_line(ix, None, scan_mode); // break out when we find a table if let Some(Item { body: ItemBody::Table(alignment_ix), .. }) = brk { let table_cols = self.allocs[alignment_ix].len(); self.tree[node_ix].item.body = ItemBody::Table(alignment_ix); // this clears out any stuff we may have appended - but there may // be a cleaner way self.tree[node_ix].child = None; self.tree.pop(); self.tree.push(); if let Some(ix) = self.parse_table(table_cols, ix, next_ix) { return ix; } } 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 trailing_backslash_pos = match brk { Some(Item { start, body: ItemBody::HardBreak(true), .. }) if bytes[start] == b'\\' => Some(start), _ => None, }; if !line_start.scan_space(4) { let ix_new = ix + line_start.bytes_scanned(); if current_container { if let Some(ix_setext) = self.parse_setext_heading(ix_new, node_ix, trailing_backslash_pos.is_some()) { if let Some(pos) = trailing_backslash_pos { self.tree.append_text(pos, pos + 1, false); } ix = ix_setext; break; } } // first check for non-empty lists, then for other interrupts let suffix = &bytes[ix_new..]; if self.scan_paragraph_interrupt(suffix, current_container) { if let Some(pos) = trailing_backslash_pos { self.tree.append_text(pos, pos + 1, false); } break; } } line_start.scan_all_space(); if line_start.is_at_eol() { if let Some(pos) = trailing_backslash_pos { self.tree.append_text(pos, pos + 1, false); } break; } ix = next_ix + line_start.bytes_scanned(); if let Some(item) = brk { self.tree.append(item); } } self.pop(ix); ix } /// Returns end ix of setext_heading on success. fn parse_setext_heading( &mut self, ix: usize, node_ix: TreeIndex, has_trailing_content: bool, ) -> Option { let bytes = self.text.as_bytes(); let (n, level) = scan_setext_heading(&bytes[ix..])?; let mut attrs = None; if let Some(cur_ix) = self.tree.cur() { let parent_ix = self.tree.peek_up().unwrap(); let header_start = self.tree[parent_ix].item.start; // Note that `self.tree[parent_ix].item.end` might be zero at this point. // Use the end position of the current node (i.e. the last known child // of the parent) instead. let header_end = self.tree[cur_ix].item.end; // extract the trailing attribute block let (content_end, attrs_) = self.extract_and_parse_heading_attribute_block(header_start, header_end); attrs = attrs_; // strip trailing whitespace 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); content_end - trailing_ws }; if attrs.is_some() { // remove trailing block attributes self.tree.truncate_siblings(new_end); } if let Some(cur_ix) = self.tree.cur() { self.tree[cur_ix].item.end = new_end; } } self.tree[node_ix].item.body = ItemBody::Heading( level, attrs.map(|attrs| self.allocs.allocate_heading(attrs)), ); Some(ix + n) } /// Parse a line of input, appending text and items to tree. /// /// Returns: index after line and an item representing the break. fn parse_line( &mut self, start: usize, end: Option, mode: TableParseMode, ) -> (usize, Option) { let bytes = self.text.as_bytes(); let bytes = match end { Some(end) => &bytes[..end], None => bytes, }; let bytes_len = bytes.len(); let mut pipes = 0; let mut last_pipe_ix = start; let mut begin_text = start; let mut backslash_escaped = false; let (final_ix, brk) = iterate_special_bytes(self.lookup_table, bytes, start, |ix, byte| { match byte { b'\n' | b'\r' => { if let TableParseMode::Active = mode { return LoopInstruction::BreakAtWith(ix, None); } let mut i = ix; let eol_bytes = scan_eol(&bytes[ix..]).unwrap(); let end_ix = ix + eol_bytes; let trailing_backslashes = scan_rev_while(&bytes[..ix], |b| b == b'\\'); if trailing_backslashes % 2 == 1 && end_ix < bytes_len { i -= 1; self.tree.append_text(begin_text, i, backslash_escaped); backslash_escaped = false; return LoopInstruction::BreakAtWith( end_ix, Some(Item { start: i, end: end_ix, body: ItemBody::HardBreak(true), }), ); } if mode == TableParseMode::Scan && pipes > 0 { // 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() { let table_head_ix = next_line_ix + line_start.bytes_scanned(); let (table_head_bytes, alignment) = scan_table_head(&bytes[table_head_ix..]); if table_head_bytes > 0 { // computing header count from number of pipes let header_count = count_header_cols(bytes, pipes, start, last_pipe_ix); // make sure they match the number of columns we find in separator line if alignment.len() == header_count { let alignment_ix = self.allocs.allocate_alignment(alignment); let end_ix = table_head_ix + table_head_bytes; return LoopInstruction::BreakAtWith( end_ix, Some(Item { start: i, end: end_ix, // must update later body: ItemBody::Table(alignment_ix), }), ); } } } } let trailing_whitespace = scan_rev_while(&bytes[..ix], is_ascii_whitespace_no_nl); if trailing_whitespace >= 2 { i -= trailing_whitespace; self.tree.append_text(begin_text, i, backslash_escaped); backslash_escaped = false; return LoopInstruction::BreakAtWith( end_ix, Some(Item { start: i, end: end_ix, body: ItemBody::HardBreak(false), }), ); } self.tree .append_text(begin_text, ix - trailing_whitespace, backslash_escaped); backslash_escaped = false; LoopInstruction::BreakAtWith( end_ix, Some(Item { start: i, end: end_ix, body: ItemBody::SoftBreak, }), ) } 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) } } else { LoopInstruction::ContinueAndSkip(0) } } 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( &self.text[start..], string_suffix, count, ix - start, mode, ); let can_close = delim_run_can_close( &self.text[start..], string_suffix, count, ix - start, mode, ); let is_valid_seq = c != b'~' || count <= 2; if (can_open || can_close) && is_valid_seq { self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; for i in 0..count { self.tree.append(Item { start: ix + i, end: ix + i + 1, body: ItemBody::MaybeEmphasis(count - i, can_open, can_close), }); } begin_text = ix + count; } LoopInstruction::ContinueAndSkip(count - 1) } b'`' => { self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; let count = 1 + scan_ch_repeat(&bytes[(ix + 1)..], b'`'); self.tree.append(Item { start: ix, end: ix + count, body: ItemBody::MaybeCode(count, false), }); begin_text = ix + count; LoopInstruction::ContinueAndSkip(count - 1) } b'<' if bytes.get(ix + 1) != Some(&b'\\') => { // Note: could detect some non-HTML cases and early escape here, but not // clear that's a win. self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + 1, body: ItemBody::MaybeHtml, }); 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'[' => { self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + 1, body: ItemBody::MaybeLinkOpen, }); begin_text = ix + 1; LoopInstruction::ContinueAndSkip(0) } b']' => { self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + 1, body: ItemBody::MaybeLinkClose(true), }); begin_text = ix + 1; LoopInstruction::ContinueAndSkip(0) } b'&' => match scan_entity(&bytes[ix..]) { (n, Some(value)) => { self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + n, body: ItemBody::SynthesizeText(self.allocs.allocate_cow(value)), }); begin_text = ix + n; LoopInstruction::ContinueAndSkip(n - 1) } _ => LoopInstruction::ContinueAndSkip(0), }, b'|' => { if ix != 0 && bytes[ix - 1] == b'\\' { LoopInstruction::ContinueAndSkip(0) } else if let TableParseMode::Active = mode { LoopInstruction::BreakAtWith(ix, None) } else { last_pipe_ix = ix; pipes += 1; 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'-' => { let count = 1 + scan_ch_repeat(&bytes[(ix + 1)..], b'-'); if count == 1 { LoopInstruction::ContinueAndSkip(0) } else { let itembody = if count == 2 { ItemBody::SynthesizeChar('–') } else if count == 3 { ItemBody::SynthesizeChar('—') } else { let (ems, ens) = match count % 6 { 0 | 3 => (count / 3, 0), 2 | 4 => (0, count / 2), 1 => (count / 3 - 1, 2), _ => (count / 3, 1), }; // – and — are 3 bytes each in utf8 let mut buf = String::with_capacity(3 * (ems + ens)); for _ in 0..ems { buf.push('—'); } for _ in 0..ens { buf.push('–'); } ItemBody::SynthesizeText(self.allocs.allocate_cow(buf.into())) }; self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + count, body: itembody, }); begin_text = ix + count; LoopInstruction::ContinueAndSkip(count - 1) } } 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_close = delim_run_can_close( &self.text[start..], string_suffix, 1, ix - start, mode, ); self.tree.append_text(begin_text, ix, backslash_escaped); backslash_escaped = false; self.tree.append(Item { start: ix, end: ix + 1, body: ItemBody::MaybeSmartQuote(c, can_open, can_close), }); begin_text = ix + 1; LoopInstruction::ContinueAndSkip(0) } _ => LoopInstruction::ContinueAndSkip(0), } }); if brk.is_none() { let trailing_whitespace = scan_rev_while(&bytes[begin_text..final_ix], is_ascii_whitespace_no_nl); // need to close text at eof self.tree.append_text( begin_text, final_ix - trailing_whitespace, backslash_escaped, ); } (final_ix, brk) } /// When start_ix is at the beginning of an HTML block of type 1 to 5, /// this will find the end of the block, adding the block itself to the /// tree and also keeping track of the lines of HTML within the block. /// /// The html_end_tag is the tag that must be found on a line to end the block. fn parse_html_block_type_1_to_5( &mut self, start_ix: usize, html_end_tag: &str, mut remaining_space: usize, mut indent: usize, ) -> usize { self.tree.append(Item { start: start_ix, end: 0, // set later body: ItemBody::HtmlBlock, }); self.tree.push(); let bytes = self.text.as_bytes(); let mut ix = start_ix; let end_ix; loop { let line_start_ix = ix; ix += scan_nextline(&bytes[ix..]); 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(), ); if n_containers < self.tree.spine_len() { end_ix = ix; break; } if self.text[line_start_ix..ix].contains(html_end_tag) { end_ix = ix; break; } let next_line_ix = ix + line_start.bytes_scanned(); if next_line_ix == self.text.len() { end_ix = next_line_ix; break; } ix = next_line_ix; remaining_space = line_start.remaining_space(); indent = 0; } self.pop(end_ix); ix } /// When start_ix is at the beginning of an HTML block of type 6 or 7, /// this will consume lines until there is a blank line and keep track of /// the HTML within the block. fn parse_html_block_type_6_or_7( &mut self, start_ix: usize, mut remaining_space: usize, mut indent: usize, ) -> usize { self.tree.append(Item { start: start_ix, end: 0, // set later body: ItemBody::HtmlBlock, }); self.tree.push(); let bytes = self.text.as_bytes(); let mut ix = start_ix; let end_ix; loop { let line_start_ix = ix; ix += scan_nextline(&bytes[ix..]); 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(), ); if n_containers < self.tree.spine_len() || line_start.is_at_eol() { end_ix = ix; break; } let next_line_ix = ix + line_start.bytes_scanned(); if next_line_ix == self.text.len() || scan_blank_line(&bytes[next_line_ix..]).is_some() { end_ix = next_line_ix; break; } ix = next_line_ix; remaining_space = line_start.remaining_space(); indent = 0; } self.pop(end_ix); ix } fn parse_indented_code_block(&mut self, start_ix: usize, mut remaining_space: usize) -> usize { self.tree.append(Item { start: start_ix, end: 0, // will get set later body: ItemBody::IndentCodeBlock, }); self.tree.push(); let bytes = self.text.as_bytes(); let mut last_nonblank_child = None; let mut last_nonblank_ix = 0; let mut end_ix = 0; self.last_line_blank = false; let mut ix = start_ix; loop { let line_start_ix = ix; ix += scan_nextline(&bytes[ix..]); self.append_code_text(remaining_space, line_start_ix, ix); // TODO(spec clarification): should we synthesize newline at EOF? if !self.last_line_blank { last_nonblank_child = self.tree.cur(); last_nonblank_ix = ix; end_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(), ); if n_containers < self.tree.spine_len() || !(line_start.scan_space(4) || line_start.is_at_eol()) { break; } let next_line_ix = ix + line_start.bytes_scanned(); if next_line_ix == self.text.len() { break; } ix = next_line_ix; remaining_space = line_start.remaining_space(); self.last_line_blank = scan_blank_line(&bytes[ix..]).is_some(); } // Trim trailing blank lines. if let Some(child) = last_nonblank_child { self.tree[child].next = None; self.tree[child].item.end = last_nonblank_ix; } self.pop(end_ix); ix } fn parse_fenced_code_block( &mut self, start_ix: usize, indent: usize, fence_ch: u8, n_fence_char: usize, ) -> usize { let bytes = self.text.as_bytes(); let mut info_start = start_ix + n_fence_char; info_start += scan_whitespace_no_nl(&bytes[info_start..]); // TODO: info strings are typically very short. wouldn't it be faster // to just do a forward scan here? let mut ix = info_start + scan_nextline(&bytes[info_start..]); let info_end = ix - scan_rev_while(&bytes[info_start..ix], is_ascii_whitespace); let info_string = unescape(&self.text[info_start..info_end], self.tree.is_in_table()); self.tree.append(Item { start: start_ix, end: 0, // will get set later body: ItemBody::FencedCodeBlock(self.allocs.allocate_cow(info_string)), }); 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(), ); 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 self.pop(ix); return ix; } line_start.scan_space(indent); let mut close_line_start = line_start.clone(); if !close_line_start.scan_space(4 - indent) { let close_ix = ix + close_line_start.bytes_scanned(); if let Some(n) = scan_closing_code_fence(&bytes[close_ix..], fence_ch, n_fence_char) { ix = close_ix + n; self.pop(ix); // try to read trailing whitespace or it will register as a completely blank line return ix + scan_blank_line(&bytes[ix..]).unwrap_or(0); } } let remaining_space = line_start.remaining_space(); ix += line_start.bytes_scanned(); let next_ix = ix + scan_nextline(&bytes[ix..]); self.append_code_text(remaining_space, ix, next_ix); ix = next_ix; } } fn parse_metadata_block( &mut self, start_ix: usize, indent: usize, metadata_block_ch: u8, ) -> usize { // metadata blocks cannot be indented if indent > 0 { return 0; } let bytes = self.text.as_bytes(); let metadata_block_kind = match metadata_block_ch { b'-' => MetadataBlockKind::YamlStyle, b'+' => MetadataBlockKind::PlusesStyle, _ => panic!("Erroneous metadata block character when parsing metadata block"), }; // 3 delimiter characters let mut ix = start_ix + 3 + scan_nextline(&bytes[start_ix + 3..]); self.tree.append(Item { start: start_ix, end: 0, // will get set later body: ItemBody::MetadataBlock(metadata_block_kind), }); 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(), ); if n_containers < self.tree.spine_len() { break; } if let (_, 0) = calc_indent(&bytes[ix..], 4) { if let Some(n) = scan_closing_metadata_block(&bytes[ix..], metadata_block_ch) { ix += n; break; } } let remaining_space = line_start.remaining_space(); ix += line_start.bytes_scanned(); let next_ix = ix + scan_nextline(&bytes[ix..]); self.append_code_text(remaining_space, ix, next_ix); ix = next_ix; } self.pop(ix); // try to read trailing whitespace or it will register as a completely blank line ix + scan_blank_line(&bytes[ix..]).unwrap_or(0) } fn append_code_text(&mut self, remaining_space: usize, start: usize, end: usize) { if remaining_space > 0 { let cow_ix = self.allocs.allocate_cow(" "[..remaining_space].into()); self.tree.append(Item { start, end: start, body: ItemBody::SynthesizeText(cow_ix), }); } if self.text.as_bytes()[end - 2] == b'\r' { // Normalize CRLF to LF self.tree.append_text(start, end - 2, false); self.tree.append_text(end - 1, end, false); } else { self.tree.append_text(start, end, false); } } /// Appends a line of HTML to the tree. fn append_html_line(&mut self, remaining_space: usize, start: usize, end: usize) { if remaining_space > 0 { let cow_ix = self.allocs.allocate_cow(" "[..remaining_space].into()); self.tree.append(Item { start, end: start, body: ItemBody::SynthesizeText(cow_ix), }); } if self.text.as_bytes()[end - 2] == b'\r' { // Normalize CRLF to LF self.tree.append(Item { start, end: end - 2, body: ItemBody::Html, }); self.tree.append(Item { start: end - 1, end, body: ItemBody::Html, }); } else { self.tree.append(Item { start, end, body: ItemBody::Html, }); } } /// Pop a container, setting its end. 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 { surgerize_tight_list(&mut self.tree, cur_ix); self.begin_list_item = None; } } /// Close a list if it's open. Also set loose if last line was blank /// and end current list if it's a lone, empty item 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 { 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 { *is_tight = false; } } self.last_line_blank = false; } } fn finish_empty_list_item(&mut self) { if let Some(begin_list_item) = self.begin_list_item { 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 { self.pop(begin_list_item); } } } } self.begin_list_item = None; } /// Continue an existing list or start a new one if there's not an open /// list that matches. fn continue_list(&mut self, start: usize, ch: u8, index: u64) { self.finish_empty_list_item(); if let Some(node_ix) = self.tree.peek_up() { if let ItemBody::List(ref mut is_tight, existing_ch, _) = self.tree[node_ix].item.body { if existing_ch == ch { if self.last_line_blank { *is_tight = false; self.last_line_blank = false; } return; } } // TODO: this is not the best choice for end; maybe get end from last list item. self.finish_list(start); } self.tree.append(Item { start, end: 0, // will get set later body: ItemBody::List(true, ch, index), }); self.tree.push(); self.last_line_blank = false; } /// Parse a thematic break. /// /// Returns index of start of next line. fn parse_hrule(&mut self, hrule_size: usize, ix: usize) -> usize { self.tree.append(Item { start: ix, end: ix + hrule_size, body: ItemBody::Rule, }); ix + hrule_size } /// Parse an ATX heading. /// /// Returns index of start of next line. fn parse_atx_heading(&mut self, start: usize, atx_level: HeadingLevel) -> usize { let mut ix = start; let heading_ix = self.tree.append(Item { start, end: 0, // set later body: ItemBody::default(), // set later }); ix += atx_level as usize; // next char is space or eol (guaranteed by scan_atx_heading) let bytes = self.text.as_bytes(); if let Some(eol_bytes) = scan_eol(&bytes[ix..]) { self.tree[heading_ix].item.end = ix + eol_bytes; self.tree[heading_ix].item.body = ItemBody::Heading(atx_level, None); return ix + eol_bytes; } // skip leading spaces let skip_spaces = scan_whitespace_no_nl(&bytes[ix..]); ix += skip_spaces; // now handle the header text let header_start = ix; let header_node_idx = self.tree.push(); // so that we can set the endpoint later // trim the trailing attribute block before parsing the entire line, if necessary let (end, content_end, attrs) = if self.options.contains(Options::ENABLE_HEADING_ATTRIBUTES) { // the start of the next line is the end of the header since the // header cannot have line breaks let header_end = header_start + scan_nextline(&bytes[header_start..]); let (content_end, attrs) = self.extract_and_parse_heading_attribute_block(header_start, header_end); self.parse_line(ix, Some(content_end), TableParseMode::Disabled); (header_end, content_end, attrs) } else { let (line_ix, line_brk) = self.parse_line(ix, None, TableParseMode::Disabled); ix = line_ix; // Backslash at end is actually hard line break if let Some(Item { start, end, body: ItemBody::HardBreak(true), }) = line_brk { self.tree.append_text(start, end, false); } (ix, ix, None) }; self.tree[header_node_idx].item.end = end; // remove trailing matter from header text let mut empty_text_node = false; if let Some(cur_ix) = self.tree.cur() { // remove closing of the ATX heading let header_text = &bytes[header_start..content_end]; let mut limit = header_text .iter() .rposition(|&b| !(b == b'\n' || b == b'\r' || b == b' ')) .map_or(0, |i| i + 1); let closer = header_text[..limit] .iter() .rposition(|&b| b != b'#') .map_or(0, |i| i + 1); if closer == 0 { limit = closer; } else { let spaces = scan_rev_while(&header_text[..closer], |b| b == b' '); if spaces > 0 { limit = closer - spaces; } } // if text is only spaces, then remove them self.tree[cur_ix].item.end = limit + header_start; // limit = 0 when text is empty after removing spaces if limit == 0 { empty_text_node = true; } } if empty_text_node { self.tree.remove_node(); } else { self.tree.pop(); } self.tree[heading_ix].item.body = ItemBody::Heading( atx_level, attrs.map(|attrs| self.allocs.allocate_heading(attrs)), ); end } /// Returns the number of bytes scanned on success. fn parse_footnote(&mut self, start: usize) -> Option { let bytes = &self.text.as_bytes()[start..]; if !bytes.starts_with(b"[^") { return None; } let (mut i, label) = if self.options.has_gfm_footnotes() { // GitHub doesn't allow footnote definition labels to contain line breaks. // It actually does allow this for link definitions under certain circumstances, // but for this it's simpler to avoid it. scan_link_label_rest( &self.text[start + 2..], &|_| { None }, self.tree.is_in_table(), )? } else { self.parse_refdef_label(start + 2)? }; if self.options.has_gfm_footnotes() && label.bytes().any(|b| b == b'\r' || b == b'\n') { // GitHub doesn't allow footnote definition labels to contain line breaks, // even if they're escaped. return None; } i += 2; if scan_ch(&bytes[i..], b':') == 0 { return None; } i += 1; self.finish_list(start); if self.options.has_gfm_footnotes() { if let Some(node_ix) = self.tree.peek_up() { if let ItemBody::FootnoteDefinition(..) = self.tree[node_ix].item.body { // finish previous footnote if it's still open self.pop(start); } } i += scan_whitespace_no_nl(&bytes[i..]); } self.allocs.footdefs.0.insert( UniCase::new(label.clone()), FootnoteDef { use_count: 0 }, ); self.tree.append(Item { start, end: 0, // will get set later // TODO: check whether the label here is strictly necessary body: ItemBody::FootnoteDefinition(self.allocs.allocate_cow(label)), }); self.tree.push(); Some(i) } /// Tries to parse a reference label, which can be interrupted by new blocks. /// On success, returns the number of bytes of the label and the label itself. fn parse_refdef_label(&self, start: usize) -> Option<(usize, CowStr<'a>)> { scan_link_label_rest( &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(); 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) || (current_container && scan_setext_heading(suffix).is_some()) { None } else { Some(bytes_scanned) } }, self.tree.is_in_table(), ) } /// 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 { return None; } let (mut i, label) = self.parse_refdef_label(start + 1)?; i += 1; if scan_ch(&bytes[i..], b':') == 0 { return None; } i += 1; let (bytecount, link_def) = self.scan_refdef(start, start + i)?; Some((bytecount + i, UniCase::new(label), link_def)) } /// Returns number of bytes and number of newlines fn scan_refdef_space(&self, bytes: &[u8], mut i: usize) -> Option<(usize, usize)> { let mut newlines = 0; loop { let whitespaces = scan_whitespace_no_nl(&bytes[i..]); i += whitespaces; if let Some(eol_bytes) = scan_eol(&bytes[i..]) { i += eol_bytes; newlines += 1; if newlines > 1 { return None; } } else { 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(); if !line_start.scan_space(4) { let suffix = &bytes[i + line_start.bytes_scanned()..]; if self.scan_paragraph_interrupt(suffix, current_container) || scan_setext_heading(suffix).is_some() { return None; } } i += line_start.bytes_scanned(); } Some((i, newlines)) } // returns (bytelength, title_str) fn scan_refdef_title<'t>(&self, text: &'t str) -> Option<(usize, CowStr<'t>)> { let bytes = text.as_bytes(); let closing_delim = match bytes.first()? { b'\'' => b'\'', b'"' => b'"', b'(' => b')', _ => return None, }; let mut bytecount = 1; let mut linestart = 1; let mut linebuf = None; while let Some(&c) = bytes.get(bytecount) { match c { b'(' if closing_delim == b')' => { // https://spec.commonmark.org/0.30/#link-title // a sequence of zero or more characters between matching parentheses ((...)), // including a ( or ) character only if it is backslash-escaped. return None; } b'\n' | b'\r' => { // push text to line buffer // this is used to strip the block formatting: // // > [first]: http://example.com " // > second" // // should get turned into `first` let linebuf = if let Some(linebuf) = &mut linebuf { linebuf } else { linebuf = Some(String::new()); linebuf.as_mut().unwrap() }; linebuf.push_str(&text[linestart..bytecount]); linebuf.push('\n'); // normalize line breaks // skip line break bytecount += 1; if c == b'\r' && bytes.get(bytecount) == Some(&b'\n') { 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(); if !line_start.scan_space(4) { let suffix = &bytes[bytecount + line_start.bytes_scanned()..]; if self.scan_paragraph_interrupt(suffix, current_container) || scan_setext_heading(suffix).is_some() { return None; } } line_start.scan_all_space(); bytecount += line_start.bytes_scanned(); linestart = bytecount; if scan_blank_line(&bytes[bytecount..]).is_some() { // blank line - not allowed return None; } } b'\\' => { bytecount += 1; if let Some(c) = bytes.get(bytecount) { if c != &b'\r' && c != &b'\n' { bytecount += 1; } } } c if c == closing_delim => { let cow = if let Some(mut linebuf) = linebuf { linebuf.push_str(&text[linestart..bytecount]); CowStr::from(linebuf) } else { CowStr::from(&text[linestart..bytecount]) }; return Some((bytecount + 1, cow)); } _ => { bytecount += 1; } } } None } /// Returns # of bytes and definition. /// Assumes the label of the reference including colon has already been scanned. fn scan_refdef(&self, span_start: usize, start: usize) -> Option<(usize, LinkDef<'a>)> { let bytes = self.text.as_bytes(); // whitespace between label and url (including up to one newline) let (mut i, _newlines) = self.scan_refdef_space(bytes, start)?; // scan link dest let (dest_length, dest) = scan_link_dest(self.text, i, LINK_MAX_NESTED_PARENS)?; if dest_length == 0 { return None; } let dest = unescape(dest, self.tree.is_in_table()); i += dest_length; // no title let mut backup = ( i - start, LinkDef { dest, title: None, span: span_start..i, }, ); // scan whitespace between dest and label let (mut i, newlines) = if let Some((new_i, mut newlines)) = self.scan_refdef_space(bytes, i) { if i == self.text.len() { newlines += 1; } if new_i == i && newlines == 0 { return None; } if newlines > 1 { return Some(backup); }; (new_i, newlines) } else { return Some(backup); }; // scan title // if this fails but newline == 1, return also a refdef without title if let Some((title_length, title)) = self.scan_refdef_title(&self.text[i..]) { i += title_length; if scan_blank_line(&bytes[i..]).is_some() { backup.0 = i - start; backup.1.span = span_start..i; backup.1.title = Some(unescape(title, self.tree.is_in_table())); return Some(backup); } } if newlines > 0 { Some(backup) } else { None } } /// Checks whether we should break a paragraph on the given input. fn scan_paragraph_interrupt(&self, bytes: &[u8], current_container: bool) -> bool { let gfm_footnote = self.options.has_gfm_footnotes(); if scan_paragraph_interrupt_no_table(bytes, current_container, gfm_footnote, &self.tree) { return true; } // pulldown-cmark allows heavy tables, that have a `|` on the header row, // to interrupt paragraphs. // // ```markdown // This is a table // | a | b | c | // |---|---|---| // | d | e | f | // // This is not a table // a | b | c // ---|---|--- // d | e | f // ``` if !self.options.contains(Options::ENABLE_TABLES) || !bytes.starts_with(b"|") { return false; } // Checking if something's a valid table or not requires looking at two lines. // First line, count unescaped pipes. let mut pipes = 0; let mut next_line_ix = 0; let mut bsesc = false; let mut last_pipe_ix = 0; for (i, &byte) in bytes.iter().enumerate() { match byte { b'\\' => { bsesc = true; continue; } b'|' if !bsesc => { pipes += 1; last_pipe_ix = i; } b'\r' | b'\n' => { next_line_ix = i + scan_eol(&bytes[i..]).unwrap(); break; } _ => {} } bsesc = false; } // scan_eol can't return 0, so this can't be zero if next_line_ix == 0 { return false; } // Scan the table head. The part that looks like: // // |---|---|---| // // Also scan any containing items, since it's on its own line, and // might be nested inside a block quote or something // // > Table: First // > | first col | second col | // > |-----------|------------| // ^ // | 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() { return false; } let table_head_ix = next_line_ix + line_start.bytes_scanned(); let (table_head_bytes, alignment) = scan_table_head(&bytes[table_head_ix..]); if table_head_bytes == 0 { return false; } // computing header count from number of pipes let header_count = count_header_cols(bytes, pipes, 0, last_pipe_ix); // make sure they match the number of columns we find in separator line alignment.len() == header_count } /// Extracts and parses a heading attribute block if exists. /// /// Returns `(end_offset_of_heading_content, (id, classes))`. /// /// If `header_end` is less than or equal to `header_start`, the given /// input is considered as empty. fn extract_and_parse_heading_attribute_block( &mut self, header_start: usize, header_end: usize, ) -> (usize, Option>) { if !self.options.contains(Options::ENABLE_HEADING_ATTRIBUTES) { return (header_end, None); } // extract the trailing attribute block let header_bytes = &self.text.as_bytes()[header_start..header_end]; let (content_len, attr_block_range_rel) = extract_attribute_block_content_from_header_text(header_bytes); let content_end = header_start + content_len; let attrs = attr_block_range_rel.and_then(|r| { parse_inside_attribute_block( &self.text[(header_start + r.start)..(header_start + r.end)], ) }); (content_end, attrs) } } /// Scanning modes for `Parser`'s `parse_line` method. #[derive(PartialEq, Eq, Copy, Clone)] enum TableParseMode { /// Inside a paragraph, scanning for table headers. Scan, /// Inside a table. Active, /// Inside a paragraph, not scanning for table headers. Disabled, } /// Computes the number of header columns in a table line by computing the number of dividing pipes /// that aren't followed or preceded by whitespace. fn count_header_cols( bytes: &[u8], mut pipes: usize, mut start: usize, last_pipe_ix: usize, ) -> usize { // was first pipe preceded by whitespace? if so, subtract one start += scan_whitespace_no_nl(&bytes[start..]); if bytes[start] == b'|' { pipes -= 1; } // was last pipe followed by whitespace? if so, sub one if scan_blank_line(&bytes[(last_pipe_ix + 1)..]).is_some() { pipes } else { pipes + 1 } } /// Checks whether we should break a paragraph on the given input. /// /// Use `FirstPass::scan_paragraph_interrupt` in any context that allows /// tables to interrupt the paragraph. fn scan_paragraph_interrupt_no_table( bytes: &[u8], current_container: bool, gfm_footnote: bool, tree: &Tree, ) -> bool { scan_eol(bytes).is_some() || scan_hrule(bytes).is_ok() || scan_atx_heading(bytes).is_some() || scan_code_fence(bytes).is_some() || scan_blockquote_start(bytes).is_some() || scan_listitem(bytes).map_or(false, |(ix, delim, index, _)| { ! current_container || tree.is_in_table() || // we don't allow interruption by either empty lists or // numbered lists starting at an index other than 1 (delim == b'*' || delim == b'-' || delim == b'+' || index == 1) && (scan_blank_line(&bytes[ix..]).is_none()) }) || bytes.starts_with(b"<") && (get_html_end_tag(&bytes[1..]).is_some() || starts_html_block_type_6(&bytes[1..])) || (gfm_footnote && bytes.starts_with(b"[^") && scan_link_label_rest( std::str::from_utf8(&bytes[2..]).unwrap(), &|_| None, tree.is_in_table(), ) .map_or(false, |(len, _)| bytes.get(2 + len) == Some(&b':'))) } /// Assumes `text_bytes` is preceded by `<`. fn get_html_end_tag(text_bytes: &[u8]) -> Option<&'static str> { static BEGIN_TAGS: &[&[u8]; 4] = &[b"pre", b"style", b"script", b"textarea"]; static ST_BEGIN_TAGS: &[&[u8]; 3] = &[b"!--", b"?", b"![CDATA["]; for (beg_tag, end_tag) in BEGIN_TAGS .iter() .zip(["", "", "", ""].iter()) { let tag_len = beg_tag.len(); if text_bytes.len() < tag_len { // begin tags are increasing in size break; } if !text_bytes[..tag_len].eq_ignore_ascii_case(beg_tag) { continue; } // Must either be the end of the line... if text_bytes.len() == tag_len { return Some(end_tag); } // ...or be followed by whitespace, newline, or '>'. let s = text_bytes[tag_len]; if is_ascii_whitespace(s) || s == b'>' { return Some(end_tag); } } for (beg_tag, end_tag) in ST_BEGIN_TAGS.iter().zip(["-->", "?>", "]]>"].iter()) { if text_bytes.starts_with(beg_tag) { return Some(end_tag); } } if text_bytes.len() > 1 && text_bytes[0] == b'!' && text_bytes[1].is_ascii_alphabetic() { Some(">") } else { None } } // 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 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 }; node_to_repoint = Some(repoint_ix); tree[repoint_ix].next = tree[child_ix].next; list_item_child = tree[child_ix].next; } } list_item = tree[listitem_ix].next; } } /// Determines whether the delimiter run starting at given index is /// left-flanking, as defined by the commonmark spec (and isn't intraword /// for _ delims). /// suffix is &s[ix..], which is passed in as an optimization, since taking /// a string subslice is O(n). fn delim_run_can_open( s: &str, suffix: &str, run_len: usize, ix: usize, mode: TableParseMode, ) -> bool { let next_char = if let Some(c) = suffix.chars().nth(run_len) { c } else { return false; }; if next_char.is_whitespace() { return false; } if ix == 0 { return true; } if mode == TableParseMode::Active { if s[..ix].ends_with('|') && !s[..ix].ends_with(r"\|") { 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) { return true; } if delim == '~' && run_len > 1 { return true; } let prev_char = s[..ix].chars().last().unwrap(); if delim == '~' && prev_char == '~' && !is_punctuation(next_char) { return true; } prev_char.is_whitespace() || is_punctuation(prev_char) && (delim != '\'' || ![']', ')'].contains(&prev_char)) } /// Determines whether the delimiter run starting at given index is /// right-flanking, as defined by the commonmark spec (and isn't intraword /// for _ delims) fn delim_run_can_close( s: &str, suffix: &str, run_len: usize, ix: usize, mode: TableParseMode, ) -> bool { if ix == 0 { return false; } let prev_char = s[..ix].chars().last().unwrap(); if prev_char.is_whitespace() { return false; } let next_char = if let Some(c) = suffix.chars().nth(run_len) { c } else { return true; }; if mode == TableParseMode::Active { if s[..ix].ends_with('|') && !s[..ix].ends_with(r"\|") { 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) { return true; } if delim == '~' && prev_char == '~' { return true; } next_char.is_whitespace() || is_punctuation(next_char) } fn create_lut(options: &Options) -> LookupTable { #[cfg(all(target_arch = "x86_64", feature = "simd"))] { LookupTable { simd: simd::compute_lookup(options), scalar: special_bytes(options), } } #[cfg(not(all(target_arch = "x86_64", feature = "simd")))] { special_bytes(options) } } fn special_bytes(options: &Options) -> [bool; 256] { let mut bytes = [false; 256]; let standard_bytes = [ b'\n', b'\r', b'*', b'_', b'&', b'\\', b'[', b']', b'<', b'!', b'`', ]; for &byte in &standard_bytes { bytes[byte as usize] = true; } if options.contains(Options::ENABLE_TABLES) { bytes[b'|' as usize] = true; } if options.contains(Options::ENABLE_STRIKETHROUGH) { bytes[b'~' as usize] = true; } if options.contains(Options::ENABLE_SMART_PUNCTUATION) { for &byte in &[b'.', b'-', b'"', b'\''] { bytes[byte as usize] = true; } } bytes } enum LoopInstruction { /// Continue looking for more special bytes, but skip next few bytes. ContinueAndSkip(usize), /// Break looping immediately, returning with the given index and value. BreakAtWith(usize, T), } #[cfg(all(target_arch = "x86_64", feature = "simd"))] struct LookupTable { simd: [u8; 16], scalar: [bool; 256], } #[cfg(not(all(target_arch = "x86_64", feature = "simd")))] 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` /// 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`. /// When the callback returns a `BreakAtWith(end_ix, opt+val)`, no more callbacks will be /// called and the function returns immediately with the return value `(end_ix, opt_val)`. /// If `BreakAtWith(..)` is never returned, this function will return the first /// index that is outside the byteslice bound and a `None` value. fn iterate_special_bytes( lut: &LookupTable, bytes: &[u8], ix: usize, callback: F, ) -> (usize, Option) where F: FnMut(usize, u8) -> LoopInstruction>, { #[cfg(all(target_arch = "x86_64", feature = "simd"))] { simd::iterate_special_bytes(lut, bytes, ix, callback) } #[cfg(not(all(target_arch = "x86_64", feature = "simd")))] { scalar_iterate_special_bytes(lut, bytes, ix, callback) } } fn scalar_iterate_special_bytes( lut: &[bool; 256], bytes: &[u8], mut ix: usize, mut callback: F, ) -> (usize, Option) where F: FnMut(usize, u8) -> LoopInstruction>, { while ix < bytes.len() { let b = bytes[ix]; if lut[b as usize] { match callback(ix, b) { LoopInstruction::ContinueAndSkip(skip) => { ix += skip; } LoopInstruction::BreakAtWith(ix, val) => { return (ix, val); } } } ix += 1; } (ix, None) } /// Split the usual heading content range and the content inside the trailing attribute block. /// /// Returns `(leading_content_len, Option)`. /// /// Note that `trailing_attr_block_range` will be empty range when the block /// is `{}`, since the range is content inside the wrapping `{` and `}`. /// /// The closing `}` of an attribute block can have trailing whitespaces. /// They are automatically trimmed when the attribute block is being searched. /// /// However, this method does not trim the trailing whitespaces of heading content. /// It is callers' responsibility to trim them if necessary. fn extract_attribute_block_content_from_header_text( heading: &[u8], ) -> (usize, Option>) { let heading_len = heading.len(); let mut ix = heading_len; ix -= scan_rev_while(heading, |b| { b == b'\n' || b == b'\r' || b == b' ' || b == b'\t' }); if ix == 0 { return (heading_len, None); } let attr_block_close = ix - 1; if heading.get(attr_block_close) != Some(&b'}') { // The last character is not `}`. No attribute blocks found. return (heading_len, None); } // move cursor before the closing right brace (`}`) ix -= 1; ix -= scan_rev_while(&heading[..ix], |b| { // Characters to be excluded: // * `{` and `}`: special characters to open and close an attribute block. // * `\\`: a special character to escape many characters and disable some syntaxes. // + Handling of this escape character differs among markdown processors. // + Escaped characters will be separate text node from neighbors, so // it is not easy to handle unescaped string and trim the trailing block. // * `<` and `>`: special characters to start and end HTML tag. // + No known processors converts `{#foo}` into // `id="<i>foo</>"` as of this writing, so hopefully // this restriction won't cause compatibility issues. // * `\n` and `\r`: a newline character. // + Setext heading can have multiple lines. However it is hard to support // attribute blocks that have newline inside, since the parsing proceeds line by // line and lines will be separate nodes even they are logically a single text. !matches!(b, b'{' | b'}' | b'<' | b'>' | b'\\' | b'\n' | b'\r') }); if ix == 0 { // `{` is not found. No attribute blocks available. return (heading_len, None); } let attr_block_open = ix - 1; if heading[attr_block_open] != b'{' { // `{` is not found. No attribute blocks available. return (heading_len, None); } (attr_block_open, Some(ix..attr_block_close)) } /// Parses an attribute block content, such as `.class1 #id .class2`. /// /// Returns `(id, classes)`. /// /// It is callers' responsibility to find opening and closing characters of the attribute /// block. Usually [`extract_attribute_block_content_from_header_text`] function does it for you. /// /// Note that this parsing requires explicit whitespace separators between /// attributes. This is intentional design with the reasons below: /// /// * to keep conversion simple and easy to understand for any possible input, /// * to avoid adding less obvious conversion rule that can reduce compatibility /// with other implementations more, and /// * to follow the major design of implementations with the support for the /// attribute blocks extension (as of this writing). /// /// See also: [`Options::ENABLE_HEADING_ATTRIBUTES`]. /// /// [`Options::ENABLE_HEADING_ATTRIBUTES`]: `crate::Options::ENABLE_HEADING_ATTRIBUTES` fn parse_inside_attribute_block(inside_attr_block: &str) -> Option> { let mut id = None; let mut classes = Vec::new(); let mut attrs = Vec::new(); for attr in inside_attr_block.split_ascii_whitespace() { // iterator returned by `str::split_ascii_whitespace` never emits empty // strings, so taking first byte won't panic. if attr.len() > 1 { let first_byte = attr.as_bytes()[0]; if first_byte == b'#' { id = Some(attr[1..].into()); } else if first_byte == b'.' { classes.push(attr[1..].into()); } else { let split = attr.split_once('='); if let Some((key, value)) = split { attrs.push((key.into(), Some(value.into()))); } else { attrs.push((attr.into(), None)); } } } } Some(HeadingAttributes { id, classes, attrs }) } #[cfg(all(target_arch = "x86_64", feature = "simd"))] mod simd { //! SIMD byte scanning logic. //! //! This module provides functions that allow walking through byteslices, calling //! provided callback functions on special bytes and their indices using SIMD. //! The byteset is defined in `compute_lookup`. //! //! The idea is to load in a chunk of 16 bytes and perform a lookup into a set of //! bytes on all the bytes in this chunk simultaneously. We produce a 16 bit bitmask //! from this and call the callback on every index corresponding to a 1 in this mask //! before moving on to the next chunk. This allows us to move quickly when there //! are no or few matches. //! //! The table lookup is inspired by this [great overview]. However, since all of the //! bytes we're interested in are ASCII, we don't quite need the full generality of //! the universal algorithm and are hence able to skip a few instructions. //! //! [great overview]: http://0x80.pl/articles/simd-byte-lookup.html use super::{LookupTable, LoopInstruction}; use crate::Options; use core::arch::x86_64::*; const VECTOR_SIZE: usize = std::mem::size_of::<__m128i>(); /// Generates a lookup table containing the bitmaps for our /// special marker bytes. This is effectively a 128 element 2d bitvector, /// that can be indexed by a four bit row index (the lower nibble) /// and a three bit column index (upper nibble). pub(super) fn compute_lookup(options: &Options) -> [u8; 16] { let mut lookup = [0u8; 16]; let standard_bytes = [ b'\n', b'\r', b'*', b'_', b'&', b'\\', b'[', b']', b'<', b'!', b'`', ]; for &byte in &standard_bytes { add_lookup_byte(&mut lookup, byte); } if options.contains(Options::ENABLE_TABLES) { add_lookup_byte(&mut lookup, b'|'); } if options.contains(Options::ENABLE_STRIKETHROUGH) { add_lookup_byte(&mut lookup, b'~'); } if options.contains(Options::ENABLE_SMART_PUNCTUATION) { for &byte in &[b'.', b'-', b'"', b'\''] { add_lookup_byte(&mut lookup, byte); } } lookup } fn add_lookup_byte(lookup: &mut [u8; 16], byte: u8) { lookup[(byte & 0x0f) as usize] |= 1 << (byte >> 4); } /// Computes a bit mask for the given byteslice starting from the given index, /// where the 16 least significant bits indicate (by value of 1) whether or not /// there is a special character at that byte position. The least significant bit /// corresponds to `bytes[ix]` and the most significant bit corresponds to /// `bytes[ix + 15]`. /// It is only safe to call this function when `bytes.len() >= ix + VECTOR_SIZE`. #[target_feature(enable = "ssse3")] #[inline] unsafe fn compute_mask(lut: &[u8; 16], bytes: &[u8], ix: usize) -> i32 { debug_assert!(bytes.len() >= ix + VECTOR_SIZE); let bitmap = _mm_loadu_si128(lut.as_ptr() as *const __m128i); // Small lookup table to compute single bit bitshifts // for 16 bytes at once. let bitmask_lookup = _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, -1, -1, -1, -1, -1, -1, -1, -1); // Load input from memory. let raw_ptr = bytes.as_ptr().add(ix) as *const __m128i; let input = _mm_loadu_si128(raw_ptr); // Compute the bitmap using the bottom nibble as an index // into the lookup table. Note that non-ascii bytes will have // their most significant bit set and will map to lookup[0]. let bitset = _mm_shuffle_epi8(bitmap, input); // Compute the high nibbles of the input using a 16-bit rightshift of four // and a mask to prevent most-significant bit issues. let higher_nibbles = _mm_and_si128(_mm_srli_epi16(input, 4), _mm_set1_epi8(0x0f)); // Create a bitmask for the bitmap by perform a left shift of the value // of the higher nibble. Bytes with their most significant set are mapped // to -1 (all ones). let bitmask = _mm_shuffle_epi8(bitmask_lookup, higher_nibbles); // Test the bit of the bitmap by AND'ing the bitmap and the mask together. let tmp = _mm_and_si128(bitset, bitmask); // Check whether the result was not null. NEQ is not a SIMD intrinsic, // but comparing to the bitmask is logically equivalent. This also prevents us // from matching any non-ASCII bytes since none of the bitmaps were all ones // (-1). let result = _mm_cmpeq_epi8(tmp, bitmask); // Return the resulting bitmask. _mm_movemask_epi8(result) } /// Calls callback on byte indices and their value. /// Breaks when callback returns LoopInstruction::BreakAtWith(ix, val). And skips the /// number of bytes in callback return value otherwise. /// Returns the final index and a possible break value. pub(super) fn iterate_special_bytes( lut: &LookupTable, bytes: &[u8], ix: usize, callback: F, ) -> (usize, Option) where F: FnMut(usize, u8) -> LoopInstruction>, { if is_x86_feature_detected!("ssse3") && bytes.len() >= VECTOR_SIZE { unsafe { simd_iterate_special_bytes(&lut.simd, bytes, ix, callback) } } else { super::scalar_iterate_special_bytes(&lut.scalar, bytes, ix, callback) } } /// Calls the callback function for every 1 in the given bitmask with /// the index `offset + ix`, where `ix` is the position of the 1 in the mask. /// Returns `Ok(ix)` to continue from index `ix`, `Err((end_ix, opt_val)` to break with /// final index `end_ix` and optional value `opt_val`. unsafe fn process_mask( mut mask: i32, bytes: &[u8], mut offset: usize, callback: &mut F, ) -> Result)> where F: FnMut(usize, u8) -> LoopInstruction>, { while mask != 0 { let mask_ix = mask.trailing_zeros() as usize; offset += mask_ix; match callback(offset, *bytes.get_unchecked(offset)) { LoopInstruction::ContinueAndSkip(skip) => { offset += skip + 1; mask = mask.wrapping_shr((skip + 1 + mask_ix) as u32); } LoopInstruction::BreakAtWith(ix, val) => return Err((ix, val)), } } Ok(offset) } #[target_feature(enable = "ssse3")] /// Important: only call this function when `bytes.len() >= 16`. Doing /// so otherwise may exhibit undefined behaviour. unsafe fn simd_iterate_special_bytes( lut: &[u8; 16], bytes: &[u8], mut ix: usize, mut callback: F, ) -> (usize, Option) where F: FnMut(usize, u8) -> LoopInstruction>, { debug_assert!(bytes.len() >= VECTOR_SIZE); let upperbound = bytes.len() - VECTOR_SIZE; while ix < upperbound { 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), Err((end_ix, val)) => return (end_ix, val), }; } if bytes.len() > ix { // shift off the bytes at start we have already scanned let mask = compute_mask(lut, bytes, upperbound) >> ix - upperbound; if let Err((end_ix, val)) = process_mask(mask, bytes, ix, &mut callback) { return (end_ix, val); } } (bytes.len(), None) } #[cfg(test)] mod simd_test { use super::super::create_lut; use super::{iterate_special_bytes, LoopInstruction}; use crate::Options; fn check_expected_indices(bytes: &[u8], expected: &[usize], skip: usize) { let mut opts = Options::empty(); opts.insert(Options::ENABLE_TABLES); opts.insert(Options::ENABLE_FOOTNOTES); opts.insert(Options::ENABLE_STRIKETHROUGH); opts.insert(Options::ENABLE_TASKLISTS); let lut = create_lut(&opts); let mut indices = vec![]; iterate_special_bytes::<_, i32>(&lut, bytes, 0, |ix, _byte_ty| { indices.push(ix); LoopInstruction::ContinueAndSkip(skip) }); assert_eq!(&indices[..], expected); } #[test] fn simple_no_match() { check_expected_indices("abcdef0123456789".as_bytes(), &[], 0); } #[test] fn simple_match() { check_expected_indices("*bcd&f0123456789".as_bytes(), &[0, 4], 0); } #[test] fn single_open_fish() { check_expected_indices("<".as_bytes(), &[0], 0); } #[test] fn long_match() { check_expected_indices("0123456789abcde~*bcd&f0".as_bytes(), &[15, 16, 20], 0); } #[test] fn border_skip() { check_expected_indices("0123456789abcde~~~~d&f0".as_bytes(), &[15, 20], 3); } #[test] fn exhaustive_search() { let chars = [ b'\n', b'\r', b'*', b'_', b'~', b'|', b'&', b'\\', b'[', b']', b'<', b'!', b'`', ]; for &c in &chars { for i in 0u8..=255 { if !chars.contains(&i) { // full match let mut buf = [i; 18]; buf[3] = c; buf[6] = c; check_expected_indices(&buf[..], &[3, 6], 0); } } } } } } pulldown-cmark-0.10.3/src/html.rs000064400000000000000000000422501046102023000147500ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! HTML renderer that takes an iterator of events as input. use std::collections::HashMap; use std::io::{self, Write}; use crate::strings::CowStr; use crate::Event::*; use crate::{Alignment, CodeBlockKind, Event, LinkType, Tag, TagEnd}; use pulldown_cmark_escape::{ escape_href, escape_html, escape_html_body_text, StrWrite, WriteWrapper, }; enum TableState { Head, Body, } struct HtmlWriter<'a, I, W> { /// Iterator supplying events. iter: I, /// Writer to write to. writer: W, /// Whether or not the last write wrote a newline. end_newline: bool, /// Whether if inside a metadata block (text should not be written) in_non_writing_block: bool, table_state: TableState, table_alignments: Vec, table_cell_index: usize, numbers: HashMap, usize>, } impl<'a, I, W> HtmlWriter<'a, I, W> where I: Iterator>, W: StrWrite, { fn new(iter: I, writer: W) -> Self { Self { iter, writer, end_newline: true, in_non_writing_block: false, table_state: TableState::Head, table_alignments: vec![], table_cell_index: 0, numbers: HashMap::new(), } } /// Writes a new line. fn write_newline(&mut self) -> io::Result<()> { self.end_newline = true; self.writer.write_str("\n") } /// Writes a buffer, and tracks whether or not a newline was written. #[inline] fn write(&mut self, s: &str) -> io::Result<()> { self.writer.write_str(s)?; if !s.is_empty() { self.end_newline = s.ends_with('\n'); } Ok(()) } fn run(mut self) -> io::Result<()> { while let Some(event) = self.iter.next() { match event { Start(tag) => { self.start_tag(tag)?; } End(tag) => { self.end_tag(tag)?; } Text(text) => { if !self.in_non_writing_block { escape_html_body_text(&mut self.writer, &text)?; self.end_newline = text.ends_with('\n'); } } Code(text) => { self.write("")?; escape_html_body_text(&mut self.writer, &text)?; self.write("")?; } Html(html) | InlineHtml(html) => { self.write(&html)?; } SoftBreak => { self.write_newline()?; } HardBreak => { self.write("
\n")?; } Rule => { if self.end_newline { self.write("
\n")?; } else { self.write("\n
\n")?; } } FootnoteReference(name) => { let len = self.numbers.len() + 1; self.write("")?; let number = *self.numbers.entry(name).or_insert(len); write!(&mut self.writer, "{}", number)?; self.write("")?; } TaskListMarker(true) => { self.write("\n")?; } TaskListMarker(false) => { self.write("\n")?; } } } Ok(()) } /// Writes the start of an HTML tag. fn start_tag(&mut self, tag: Tag<'a>) -> io::Result<()> { match tag { Tag::HtmlBlock => Ok(()), Tag::Paragraph => { if self.end_newline { self.write("

") } else { self.write("\n

") } } Tag::Heading { level, id, classes, attrs, } => { if self.end_newline { self.end_newline = false; self.write("<")?; } else { self.write("\n<")?; } write!(&mut self.writer, "{}", level)?; if let Some(id) = id { self.write(" id=\"")?; escape_html(&mut self.writer, &id)?; self.write("\"")?; } let mut classes = classes.iter(); if let Some(class) = classes.next() { self.write(" class=\"")?; escape_html(&mut self.writer, class)?; for class in classes { self.write(" ")?; escape_html(&mut self.writer, class)?; } self.write("\"")?; } for (attr, value) in attrs { self.write(" ")?; escape_html(&mut self.writer, &attr)?; if let Some(val) = value { self.write("=\"")?; escape_html(&mut self.writer, &val)?; self.write("\"")?; } else { self.write("=\"\"")?; } } self.write(">") } Tag::Table(alignments) => { self.table_alignments = alignments; self.write("") } Tag::TableHead => { self.table_state = TableState::Head; self.table_cell_index = 0; self.write("") } Tag::TableRow => { self.table_cell_index = 0; self.write("") } Tag::TableCell => { match self.table_state { TableState::Head => { self.write(" { self.write(" self.write(" style=\"text-align: left\">"), Some(&Alignment::Center) => self.write(" style=\"text-align: center\">"), Some(&Alignment::Right) => self.write(" style=\"text-align: right\">"), _ => self.write(">"), } } Tag::BlockQuote => { if self.end_newline { self.write("
\n") } else { self.write("\n
\n") } } Tag::CodeBlock(info) => { if !self.end_newline { self.write_newline()?; } match info { CodeBlockKind::Fenced(info) => { let lang = info.split(' ').next().unwrap(); if lang.is_empty() { self.write("
")
                        } else {
                            self.write("
")
                        }
                    }
                    CodeBlockKind::Indented => self.write("
"),
                }
            }
            Tag::List(Some(1)) => {
                if self.end_newline {
                    self.write("
    \n") } else { self.write("\n
      \n") } } Tag::List(Some(start)) => { if self.end_newline { self.write("
        \n") } Tag::List(None) => { if self.end_newline { self.write("
\n")?; } TagEnd::TableHead => { self.write("\n")?; self.table_state = TableState::Body; } TagEnd::TableRow => { self.write("\n")?; } TagEnd::TableCell => { match self.table_state { TableState::Head => { self.write("")?; } TableState::Body => { self.write("")?; } } self.table_cell_index += 1; } TagEnd::BlockQuote => { self.write("\n")?; } TagEnd::CodeBlock => { self.write("\n")?; } TagEnd::List(true) => { self.write("\n")?; } TagEnd::List(false) => { self.write("\n")?; } TagEnd::Item => { self.write("\n")?; } TagEnd::Emphasis => { self.write("")?; } TagEnd::Strong => { self.write("")?; } TagEnd::Strikethrough => { self.write("")?; } TagEnd::Link => { self.write("")?; } TagEnd::Image => (), // shouldn't happen, handled in start TagEnd::FootnoteDefinition => { self.write("\n")?; } TagEnd::MetadataBlock(_) => { self.in_non_writing_block = false; } } Ok(()) } // run raw text, consuming end tag fn raw_text(&mut self) -> io::Result<()> { let mut nest = 0; while let Some(event) = self.iter.next() { match event { Start(_) => nest += 1, End(_) => { if nest == 0 { break; } nest -= 1; } Html(_) => {} InlineHtml(text) | Code(text) | Text(text) => { // Don't use escape_html_body_text here. // The output of this function is used in the `alt` attribute. escape_html(&mut self.writer, &text)?; self.end_newline = text.ends_with('\n'); } SoftBreak | HardBreak | Rule => { self.write(" ")?; } FootnoteReference(name) => { let len = self.numbers.len() + 1; let number = *self.numbers.entry(name).or_insert(len); write!(&mut self.writer, "[{}]", number)?; } TaskListMarker(true) => self.write("[x]")?, TaskListMarker(false) => self.write("[ ]")?, } } Ok(()) } } /// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and /// push it to a `String`. /// /// # Examples /// /// ``` /// use pulldown_cmark::{html, Parser}; /// /// let markdown_str = r#" /// hello /// ===== /// /// * alpha /// * beta /// "#; /// let parser = Parser::new(markdown_str); /// /// let mut html_buf = String::new(); /// html::push_html(&mut html_buf, parser); /// /// assert_eq!(html_buf, r#"

hello

///
    ///
  • alpha
  • ///
  • beta
  • ///
/// "#); /// ``` pub fn push_html<'a, I>(s: &mut String, iter: I) where I: Iterator>, { HtmlWriter::new(iter, s).run().unwrap(); } /// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and /// write it out to a writable stream. /// /// **Note**: using this function with an unbuffered writer like a file or socket /// will result in poor performance. Wrap these in a /// [`BufWriter`](https://doc.rust-lang.org/std/io/struct.BufWriter.html) to /// prevent unnecessary slowdowns. /// /// # Examples /// /// ``` /// use pulldown_cmark::{html, Parser}; /// use std::io::Cursor; /// /// let markdown_str = r#" /// hello /// ===== /// /// * alpha /// * beta /// "#; /// let mut bytes = Vec::new(); /// let parser = Parser::new(markdown_str); /// /// html::write_html(Cursor::new(&mut bytes), parser); /// /// assert_eq!(&String::from_utf8_lossy(&bytes)[..], r#"

hello

///
    ///
  • alpha
  • ///
  • beta
  • ///
/// "#); /// ``` pub fn write_html<'a, I, W>(writer: W, iter: I) -> io::Result<()> where I: Iterator>, W: Write, { HtmlWriter::new(iter, WriteWrapper(writer)).run() } pulldown-cmark-0.10.3/src/lib.rs000064400000000000000000000374251046102023000145620ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Pull parser for [CommonMark](https://commonmark.org). This crate provides a [Parser](struct.Parser.html) struct //! which is an iterator over [Event](enum.Event.html)s. This iterator can be used //! directly, or to output HTML using the [HTML module](html/index.html). //! //! By default, only CommonMark features are enabled. To use extensions like tables, //! footnotes or task lists, enable them by setting the corresponding flags in the //! [Options](struct.Options.html) struct. //! //! # Example //! ```rust //! use pulldown_cmark::{Parser, Options}; //! //! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example."; //! //! // Set up options and parser. Strikethroughs are not part of the CommonMark standard //! // and we therefore must enable it explicitly. //! let mut options = Options::empty(); //! options.insert(Options::ENABLE_STRIKETHROUGH); //! let parser = Parser::new_ext(markdown_input, options); //! //! # #[cfg(feature = "html")] { //! // Write to String buffer. //! let mut html_output = String::new(); //! pulldown_cmark::html::push_html(&mut html_output, parser); //! //! // Check that the output is what we expected. //! let expected_html = "

Hello world, this is a complicated very simple example.

\n"; //! assert_eq!(expected_html, &html_output); //! # } //! ``` //! //! Note that consecutive text events can happen due to the manner in which the //! parser evaluates the source. A utility `TextMergeStream` exists to improve //! the comfort of iterating the events: //! //! ```rust //! use pulldown_cmark::{Event, Parser, TextMergeStream}; //! //! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example."; //! //! let iterator = TextMergeStream::new(Parser::new(markdown_input)); //! //! for event in iterator { //! match event { //! Event::Text(text) => println!("{}", text), //! _ => {} //! } //! } //! ``` //! // 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))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] // Forbid unsafe code unless the SIMD feature is enabled. #![cfg_attr(not(feature = "simd"), forbid(unsafe_code))] #![warn(missing_debug_implementations)] #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "html")] pub mod html; pub mod utils; mod entities; mod firstpass; mod linklabel; mod parse; mod puncttable; mod scanners; mod strings; mod tree; use std::fmt::Display; pub use crate::parse::{ BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback, OffsetIter, Parser, RefDefs, }; pub use crate::strings::{CowStr, InlineStr}; pub use crate::utils::*; /// Codeblock kind. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum CodeBlockKind<'a> { Indented, /// The value contained in the tag describes the language of the code, which may be empty. #[cfg_attr(feature = "serde", serde(borrow))] Fenced(CowStr<'a>), } impl<'a> CodeBlockKind<'a> { pub fn is_indented(&self) -> bool { matches!(*self, CodeBlockKind::Indented) } pub fn is_fenced(&self) -> bool { matches!(*self, CodeBlockKind::Fenced(_)) } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MetadataBlockKind { YamlStyle, PlusesStyle, } /// Tags for elements that can contain other elements. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Tag<'a> { /// A paragraph of text and other inline elements. Paragraph, /// A heading, with optional identifier, classes and custom attributes. /// 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`). Heading { level: HeadingLevel, id: Option>, classes: Vec>, /// The first item of the tuple is the attr and second one the value. attrs: Vec<(CowStr<'a>, Option>)>, }, BlockQuote, /// A code block. CodeBlock(CodeBlockKind<'a>), /// A HTML block. HtmlBlock, /// A list. If the list is ordered the field indicates the number of the first item. /// Contains only list items. List(Option), // TODO: add delim and tight for ast (not needed for html) /// A list item. Item, /// A footnote definition. The value contained is the footnote's label by which it can /// be referred to. #[cfg_attr(feature = "serde", serde(borrow))] FootnoteDefinition(CowStr<'a>), /// A table. Contains a vector describing the text-alignment for each of its columns. Table(Vec), /// A table header. Contains only `TableCell`s. Note that the table body starts immediately /// after the closure of the `TableHead` tag. There is no `TableBody` tag. TableHead, /// A table row. Is used both for header rows as body rows. Contains only `TableCell`s. TableRow, TableCell, // span-level tags Emphasis, Strong, Strikethrough, /// A link. Link { link_type: LinkType, dest_url: CowStr<'a>, title: CowStr<'a>, /// Identifier of reference links, e.g. `world` in the link `[hello][world]`. id: CowStr<'a>, }, /// An image. The first field is the link type, the second the destination URL and the third is a title, /// the fourth is the link identifier. Image { link_type: LinkType, dest_url: CowStr<'a>, title: CowStr<'a>, /// Identifier of reference links, e.g. `world` in the link `[hello][world]`. id: CowStr<'a>, }, /// A metadata block. MetadataBlock(MetadataBlockKind), } impl<'a> Tag<'a> { pub fn to_end(&self) -> TagEnd { match self { Tag::Paragraph => TagEnd::Paragraph, Tag::Heading { level, .. } => TagEnd::Heading(*level), Tag::BlockQuote => TagEnd::BlockQuote, Tag::CodeBlock(_) => TagEnd::CodeBlock, Tag::HtmlBlock => TagEnd::HtmlBlock, Tag::List(number) => TagEnd::List(number.is_some()), Tag::Item => TagEnd::Item, Tag::FootnoteDefinition(_) => TagEnd::FootnoteDefinition, Tag::Table(_) => TagEnd::Table, Tag::TableHead => TagEnd::TableHead, Tag::TableRow => TagEnd::TableRow, Tag::TableCell => TagEnd::TableCell, Tag::Emphasis => TagEnd::Emphasis, Tag::Strong => TagEnd::Strong, Tag::Strikethrough => TagEnd::Strikethrough, Tag::Link { .. } => TagEnd::Link, Tag::Image { .. } => TagEnd::Image, Tag::MetadataBlock(kind) => TagEnd::MetadataBlock(*kind), } } } /// The end of a `Tag`. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TagEnd { Paragraph, Heading(HeadingLevel), BlockQuote, CodeBlock, HtmlBlock, /// A list, `true` for ordered lists. List(bool), Item, FootnoteDefinition, Table, TableHead, TableRow, TableCell, Emphasis, Strong, Strikethrough, Link, Image, MetadataBlock(MetadataBlockKind), } impl<'a> From> for TagEnd { fn from(value: Tag) -> Self { value.to_end() } } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum HeadingLevel { H1 = 1, H2, H3, H4, H5, H6, } impl Display for HeadingLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::H1 => write!(f, "h1"), Self::H2 => write!(f, "h2"), Self::H3 => write!(f, "h3"), Self::H4 => write!(f, "h4"), Self::H5 => write!(f, "h5"), Self::H6 => write!(f, "h6"), } } } /// Returned when trying to convert a `usize` into a `Heading` but it fails /// because the usize isn't a valid heading level #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct InvalidHeadingLevel(usize); impl TryFrom for HeadingLevel { type Error = InvalidHeadingLevel; fn try_from(value: usize) -> Result { match value { 1 => Ok(Self::H1), 2 => Ok(Self::H2), 3 => Ok(Self::H3), 4 => Ok(Self::H4), 5 => Ok(Self::H5), 6 => Ok(Self::H6), _ => Err(InvalidHeadingLevel(value)), } } } /// Type specifier for inline links. See [the Tag::Link](enum.Tag.html#variant.Link) for more information. #[derive(Clone, Debug, PartialEq, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum LinkType { /// Inline link like `[foo](bar)` Inline, /// Reference link like `[foo][bar]` Reference, /// Reference without destination in the document, but resolved by the broken_link_callback ReferenceUnknown, /// Collapsed link like `[foo][]` Collapsed, /// Collapsed link without destination in the document, but resolved by the broken_link_callback CollapsedUnknown, /// Shortcut link like `[foo]` Shortcut, /// Shortcut without destination in the document, but resolved by the broken_link_callback ShortcutUnknown, /// Autolink like `` Autolink, /// Email address in autolink like `` Email, } impl LinkType { /// Map the link type to an equivalent _Unknown link type. fn to_unknown(self) -> Self { match self { LinkType::Reference => LinkType::ReferenceUnknown, LinkType::Collapsed => LinkType::CollapsedUnknown, LinkType::Shortcut => LinkType::ShortcutUnknown, _ => unreachable!(), } } } /// Markdown events that are generated in a preorder traversal of the document /// tree, with additional `End` events whenever all of an inner node's children /// have been visited. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Event<'a> { /// Start of a tagged element. Events that are yielded after this event /// and before its corresponding `End` event are inside this element. /// Start and end events are guaranteed to be balanced. #[cfg_attr(feature = "serde", serde(borrow))] Start(Tag<'a>), /// End of a tagged element. End(TagEnd), /// A text node. #[cfg_attr(feature = "serde", serde(borrow))] Text(CowStr<'a>), /// An inline code node. #[cfg_attr(feature = "serde", serde(borrow))] Code(CowStr<'a>), /// An HTML node. #[cfg_attr(feature = "serde", serde(borrow))] Html(CowStr<'a>), /// An inline HTML node. #[cfg_attr(feature = "serde", serde(borrow))] InlineHtml(CowStr<'a>), /// A reference to a footnote with given label, which may or may not be defined /// by an event with a `Tag::FootnoteDefinition` tag. Definitions and references to them may /// occur in any order. #[cfg_attr(feature = "serde", serde(borrow))] FootnoteReference(CowStr<'a>), /// A soft line break. SoftBreak, /// A hard line break. HardBreak, /// A horizontal ruler. Rule, /// A task list marker, rendered as a checkbox in HTML. Contains a true when it is checked. TaskListMarker(bool), } /// Table column text alignment. #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Alignment { /// Default text alignment. None, Left, Center, Right, } bitflags::bitflags! { /// Option struct containing flags for enabling extra features /// that are not part of the CommonMark spec. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Options: u32 { const ENABLE_TABLES = 1 << 1; /// GitHub-compatible footnote syntax. /// /// Footnotes are referenced with the syntax `[^IDENT]`, /// and defined with an identifier followed by a colon at top level. /// /// --- /// /// ```markdown /// Footnote referenced [^1]. /// /// [^1]: footnote defined /// ``` /// /// Footnote referenced [^1]. /// /// [^1]: footnote defined const ENABLE_FOOTNOTES = 1 << 2; const ENABLE_STRIKETHROUGH = 1 << 3; const ENABLE_TASKLISTS = 1 << 4; const ENABLE_SMART_PUNCTUATION = 1 << 5; /// Extension to allow headings to have ID and classes. /// /// `# text { #id .class1 .class2 myattr other_attr=myvalue }` /// is interpreted as a level 1 heading /// with the content `text`, ID `id`, classes `class1` and `class2` and /// custom attributes `myattr` (without value) and /// `other_attr` with value `myvalue`. /// Note that ID, classes, and custom attributes should be space-separated. const ENABLE_HEADING_ATTRIBUTES = 1 << 6; /// Metadata blocks in YAML style, i.e.: /// - starting with a `---` line /// - ending with a `---` or `...` line const ENABLE_YAML_STYLE_METADATA_BLOCKS = 1 << 7; /// Metadata blocks delimited by: /// - `+++` line at start /// - `+++` line at end const ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS = 1 << 8; /// Older footnote syntax. This flag implies `ENABLE_FOOTNOTES`, changing it to use an /// older syntax instead of the new, default, GitHub-compatible syntax. /// /// New syntax is different from the old syntax regarding /// indentation, nesting, and footnote references with no definition: /// /// ```markdown /// [^1]: In new syntax, this is two footnote definitions. /// [^2]: In old syntax, this is a single footnote definition with two lines. /// /// [^3]: /// /// In new syntax, this is a footnote with two paragraphs. /// /// In old syntax, this is a footnote followed by a code block. /// /// In new syntax, this undefined footnote definition renders as /// literal text [^4]. In old syntax, it creates a dangling link. /// ``` const ENABLE_OLD_FOOTNOTES = (1 << 9) | (1 << 2); } } impl Options { pub(crate) fn has_gfm_footnotes(&self) -> bool { self.contains(Options::ENABLE_FOOTNOTES) && !self.contains(Options::ENABLE_OLD_FOOTNOTES) } } pulldown-cmark-0.10.3/src/linklabel.rs000064400000000000000000000143321046102023000157410ustar 00000000000000// Copyright 2018 Google LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Link label parsing and matching. use unicase::UniCase; use crate::scanners::{is_ascii_whitespace, scan_eol, is_ascii_punctuation}; use crate::strings::CowStr; #[derive(Debug)] pub(crate) enum ReferenceLabel<'a> { Link(CowStr<'a>), Footnote(CowStr<'a>), } pub(crate) type LinkLabel<'a> = UniCase>; pub(crate) type FootnoteLabel<'a> = UniCase>; /// Assumes the opening bracket has already been scanned. /// The line break handler determines what happens when a linebreak /// is found. It is passed the bytes following the line break and /// either returns `Some(k)`, where `k` is the number of bytes to skip, /// or `None` to abort parsing the label. /// Returns the number of bytes read (including closing bracket) and label on success. pub(crate) fn scan_link_label_rest<'t>( text: &'t str, linebreak_handler: &dyn Fn(&[u8]) -> Option, is_in_table: bool, ) -> Option<(usize, CowStr<'t>)> { let bytes = text.as_bytes(); let mut ix = 0; let mut only_white_space = true; let mut codepoints = 0; // no worries, doesn't allocate until we push things onto it let mut label = String::new(); let mut mark = 0; loop { if codepoints >= 1000 { return None; } match *bytes.get(ix)? { b'[' => return None, b']' => break, // Backslash escapes in link references are normally untouched, but // tables are an exception, because they're parsed as-if the tables // were parsed in a discrete pass, changing `\|` to `|`, and then // passing the changed string to the inline parser. b'|' if is_in_table && ix != 0 && bytes.get(ix - 1) == Some(&b'\\') => { // only way to reach this spot is to have `\\|` (even number of `\` before `|`) label.push_str(&text[mark..ix - 1]); label.push('|'); ix += 1; only_white_space = false; mark = ix; } b'\\' if is_in_table && bytes.get(ix + 1) == Some(&b'|') => { // only way to reach this spot is to have `\|` (odd number of `\` before `|`) label.push_str(&text[mark..ix]); label.push('|'); ix += 2; codepoints += 1; only_white_space = false; mark = ix; } b'\\' if is_ascii_punctuation(*bytes.get(ix + 1)?) => { ix += 2; codepoints += 2; only_white_space = false; } b if is_ascii_whitespace(b) => { // normalize labels by collapsing whitespaces, including linebreaks let mut whitespaces = 0; let mut linebreaks = 0; let whitespace_start = ix; while ix < bytes.len() && is_ascii_whitespace(bytes[ix]) { if let Some(eol_bytes) = scan_eol(&bytes[ix..]) { linebreaks += 1; if linebreaks > 1 { return None; } ix += eol_bytes; ix += linebreak_handler(&bytes[ix..])?; whitespaces += 2; // indicate that we need to replace } else { whitespaces += if bytes[ix] == b' ' { 1 } else { 2 }; ix += 1; } } if whitespaces > 1 { label.push_str(&text[mark..whitespace_start]); label.push(' '); mark = ix; codepoints += ix - whitespace_start; } else { codepoints += 1; } } b => { only_white_space = false; ix += 1; if b & 0b1000_0000 != 0 { codepoints += 1; } } } } if only_white_space { None } else { let cow = if mark == 0 { let asciiws = &[' ', '\r', '\n', '\t'][..]; text[..ix].trim_matches(asciiws).into() } else { label.push_str(&text[mark..ix]); while matches!(label.as_bytes().last(), Some(&b' ' | &b'\r' | &b'\n' | &b'\t')) { label.pop(); } while matches!(label.as_bytes().first(), Some(&b' ' | &b'\r' | &b'\n' | &b'\t')) { label.remove(0); } label.into() }; Some((ix + 1, cow)) } } #[cfg(test)] mod test { use super::scan_link_label_rest; #[test] fn whitespace_normalization() { let input = "«\t\tBlurry Eyes\t\t»][blurry_eyes]"; let expected_output = "« Blurry Eyes »"; // regular spaces! let (_bytes, normalized_label) = scan_link_label_rest(input, &|_| None, false).unwrap(); assert_eq!(expected_output, normalized_label.as_ref()); } #[test] fn return_carriage_linefeed_ok() { let input = "hello\r\nworld\r\n]"; assert!(scan_link_label_rest(input, &|_| Some(0), false).is_some()); } } pulldown-cmark-0.10.3/src/main.rs000064400000000000000000000151121046102023000147250ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Command line tool to exercise pulldown-cmark. #![forbid(unsafe_code)] use pulldown_cmark::{html, BrokenLink, Options, Parser}; use std::env; use std::fs::File; use std::io::{self, Read}; use std::path::PathBuf; fn dry_run(text: &str, opts: Options, broken_links: &mut Vec>) { let p = Parser::new_with_broken_link_callback( text, opts, Some(|link: BrokenLink<'_>| { broken_links.push(link.into_static()); None }), ); let count = p.count(); println!("{} events", count); } fn print_events(text: &str, opts: Options, broken_links: &mut Vec>) { let parser = Parser::new_with_broken_link_callback( text, opts, Some(|link: BrokenLink<'_>| { broken_links.push(link.into_static()); None }), ) .into_offset_iter(); for (event, range) in parser { println!("{:?}: {:?}", range, event); } println!("EOF"); } fn brief(program: &str) -> String { format!( "Usage: {} [options]\n\n{}", program, "Reads markdown from file or standard input and emits HTML.", ) } pub fn main() -> std::io::Result<()> { let args: Vec<_> = env::args().collect(); let mut opts = getopts::Options::new(); opts.optflag("h", "help", "this help message"); opts.optflag("d", "dry-run", "dry run, produce no output"); opts.optflag("e", "events", "print event sequence instead of rendering"); opts.optflag("T", "enable-tables", "enable GitHub-style tables"); opts.optflag("F", "enable-footnotes", "enable GitHub-style footnotes"); opts.optflag("", "enable-old-footnotes", "enable Hoedown-style footnotes"); opts.optflag( "S", "enable-strikethrough", "enable GitHub-style strikethrough", ); opts.optflag("L", "enable-tasklists", "enable GitHub-style task lists"); opts.optflag("P", "enable-smart-punctuation", "enable smart punctuation"); opts.optflag( "H", "enable-heading-attributes", "enable heading attributes", ); opts.optflag("M", "enable-metadata-blocks", "enable metadata blocks"); opts.optflag( "R", "reject-broken-links", "fail if input file has broken links", ); let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { eprintln!("{}\n{}", f, opts.usage(&brief(&args[0]))); std::process::exit(1); } }; if matches.opt_present("help") { println!("{}", opts.usage(&brief(&args[0]))); return Ok(()); } let mut opts = Options::empty(); if matches.opt_present("enable-tables") { opts.insert(Options::ENABLE_TABLES); } if matches.opt_present("enable-footnotes") { opts.insert(Options::ENABLE_FOOTNOTES); } if matches.opt_present("enable-old-footnotes") { opts.insert(Options::ENABLE_OLD_FOOTNOTES); } if matches.opt_present("enable-strikethrough") { opts.insert(Options::ENABLE_STRIKETHROUGH); } if matches.opt_present("enable-tasklists") { opts.insert(Options::ENABLE_TASKLISTS); } if matches.opt_present("enable-smart-punctuation") { opts.insert(Options::ENABLE_SMART_PUNCTUATION); } if matches.opt_present("enable-heading-attributes") { opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); } if matches.opt_present("enable-metadata-blocks") { opts.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS); opts.insert(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS); } let mut input = String::new(); let mut broken_links = vec![]; if !&matches.free.is_empty() { for filename in &matches.free { let real_path = PathBuf::from(filename); let mut f = File::open(&real_path).expect("file not found"); f.read_to_string(&mut input) .expect("something went wrong reading the file"); if matches.opt_present("events") { print_events(&input, opts, &mut broken_links); } else if matches.opt_present("dry-run") { dry_run(&input, opts, &mut broken_links); } else { pulldown_cmark(&input, opts, &mut broken_links); } } } else { let _ = io::stdin().lock().read_to_string(&mut input); if matches.opt_present("events") { print_events(&input, opts, &mut broken_links); } else if matches.opt_present("dry-run") { dry_run(&input, opts, &mut broken_links); } else { pulldown_cmark(&input, opts, &mut broken_links); } } if matches.opt_present("reject-broken-links") && !broken_links.is_empty() { eprintln!("Error: {} broken links:", broken_links.len()); for link in broken_links { let start = link.span.start; let end = link.span.end; let reference = link.reference; eprintln!("[{start}-{end}]: {reference}"); } std::process::exit(1); } Ok(()) } pub fn pulldown_cmark(input: &str, opts: Options, broken_links: &mut Vec>) { let mut p = Parser::new_with_broken_link_callback( input, opts, Some(|link: BrokenLink<'_>| { broken_links.push(link.into_static()); None }), ); let stdio = io::stdout(); let buffer = std::io::BufWriter::with_capacity(1024 * 1024, stdio.lock()); let _ = html::write_html(buffer, &mut p); } pulldown-cmark-0.10.3/src/parse.rs000064400000000000000000002460711046102023000151250ustar 00000000000000// Copyright 2017 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Tree-based two pass parser. use std::cmp::{max, min}; use std::collections::{HashMap, VecDeque}; use std::iter::FusedIterator; use std::num::NonZeroUsize; use std::ops::{Index, Range}; use unicase::UniCase; use crate::firstpass::run_first_pass; use crate::linklabel::{scan_link_label_rest, FootnoteLabel, LinkLabel, ReferenceLabel}; use crate::strings::CowStr; use crate::tree::{Tree, TreeIndex}; use crate::{scanners::*, MetadataBlockKind}; use crate::{Alignment, CodeBlockKind, Event, HeadingLevel, LinkType, Options, Tag, TagEnd}; // Allowing arbitrary depth nested parentheses inside link destinations // can create denial of service vulnerabilities if we're not careful. // The simplest countermeasure is to limit their depth, which is // explicitly allowed by the spec as long as the limit is at least 3: // https://spec.commonmark.org/0.29/#link-destination pub(crate) const LINK_MAX_NESTED_PARENS: usize = 5; #[derive(Debug, Default, Clone, Copy)] pub(crate) struct Item { pub start: usize, pub end: usize, pub body: ItemBody, } #[derive(Debug, PartialEq, Clone, Copy)] #[derive(Default)] pub(crate) enum ItemBody { Paragraph, Text { backslash_escaped: bool }, SoftBreak, // true = is backlash HardBreak(bool), // These are possible inline items, need to be resolved in second pass. // repeats, can_open, can_close MaybeEmphasis(usize, bool, bool), // quote byte, can_open, can_close MaybeSmartQuote(u8, bool, bool), MaybeCode(usize, bool), // number of backticks, preceded by backslash MaybeHtml, MaybeLinkOpen, // bool indicates whether or not the preceding section could be a reference MaybeLinkClose(bool), MaybeImage, // These are inline items after resolution. Emphasis, Strong, Strikethrough, Code(CowIndex), Link(LinkIndex), Image(LinkIndex), FootnoteReference(CowIndex), TaskListMarker(bool), // true for checked Rule, Heading(HeadingLevel, Option), // heading level FencedCodeBlock(CowIndex), IndentCodeBlock, HtmlBlock, InlineHtml, Html, OwnedHtml(CowIndex), BlockQuote, List(bool, u8, u64), // is_tight, list character, list start index ListItem(usize), // indent level SynthesizeText(CowIndex), SynthesizeChar(char), FootnoteDefinition(CowIndex), MetadataBlock(MetadataBlockKind), // Tables Table(AlignmentIndex), TableHead, TableRow, TableCell, // Dummy node at the top of the tree - should not be used otherwise! #[default] Root, } impl ItemBody { fn is_inline(&self) -> bool { matches!( *self, ItemBody::MaybeEmphasis(..) | ItemBody::MaybeSmartQuote(..) | ItemBody::MaybeHtml | ItemBody::MaybeCode(..) | ItemBody::MaybeLinkOpen | ItemBody::MaybeLinkClose(..) | ItemBody::MaybeImage ) } fn is_block(&self) -> bool { matches!( *self, ItemBody::Paragraph | ItemBody::BlockQuote | ItemBody::List(..) | ItemBody::ListItem(..) | ItemBody::HtmlBlock | ItemBody::Table(..) | ItemBody::TableHead | ItemBody::TableRow | ItemBody::TableCell | ItemBody::Heading(..) | ItemBody::Rule ) } } #[derive(Debug)] pub struct BrokenLink<'a> { pub span: std::ops::Range, pub link_type: LinkType, pub reference: CowStr<'a>, } /// Markdown event iterator. pub struct Parser<'input, F = DefaultBrokenLinkCallback> { text: &'input str, options: Options, tree: Tree, allocs: Allocations<'input>, broken_link_callback: Option, html_scan_guard: HtmlScanGuard, // https://github.com/pulldown-cmark/pulldown-cmark/issues/844 // Consider this example: // // [x]: xxx... // [x] // [x] // [x] // // Which expands to this HTML: // // x // x // x // // This is quadratic growth, because it's filling in the area of a square. // To prevent this, track how much it's expanded and limit it. link_ref_expansion_limit: usize, // used by inline passes. store them here for reuse inline_stack: InlineStack, link_stack: LinkStack, } impl<'input, F> std::fmt::Debug for Parser<'input, F> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Only print the fields that have public types. f.debug_struct("Parser") .field("text", &self.text) .field("options", &self.options) .field( "broken_link_callback", &self.broken_link_callback.as_ref().map(|_| ..), ) .finish() } } impl<'a> BrokenLink<'a> { /// Moves the link into version with a static lifetime. /// /// The `reference` member is cloned to a Boxed or Inline version. pub fn into_static(self) -> BrokenLink<'static> { BrokenLink { span: self.span.clone(), link_type: self.link_type, reference: self.reference.into_string().into(), } } } impl<'input> Parser<'input, DefaultBrokenLinkCallback> { /// Creates a new event iterator for a markdown string without any options enabled. pub fn new(text: &'input str) -> Self { Self::new_ext(text, Options::empty()) } /// Creates a new event iterator for a markdown string with given options. pub fn new_ext(text: &'input str, options: Options) -> Self { Self::new_with_broken_link_callback(text, options, None) } } impl<'input, F: BrokenLinkCallback<'input>> Parser<'input, F> { /// In case the parser encounters any potential links that have a broken /// reference (e.g `[foo]` when there is no `[foo]: ` entry at the bottom) /// the provided callback will be called with the reference name, /// and the returned pair will be used as the link URL and title if it is not /// `None`. pub fn new_with_broken_link_callback( text: &'input str, options: Options, broken_link_callback: Option, ) -> Self { let (mut tree, allocs) = run_first_pass(text, options); tree.reset(); let inline_stack = Default::default(); let link_stack = Default::default(); let html_scan_guard = Default::default(); Parser { text, options, tree, allocs, broken_link_callback, inline_stack, link_stack, html_scan_guard, // always allow 100KiB link_ref_expansion_limit: text.len().max(100_000), } } /// Returns a reference to the internal `RefDefs` object, which provides access /// to the internal map of reference definitions. pub fn reference_definitions(&self) -> &RefDefs<'_> { &self.allocs.refdefs } /// Use a link label to fetch a type, url, and title. /// /// This function enforces the [`link_ref_expansion_limit`]. /// If it returns Some, it also consumes some of the fuel. /// If we're out of fuel, it immediately returns None. /// /// The URL and title are found in the [`RefDefs`] map. /// If they're not there, and a callback was provided by the user, /// the [`broken_link_callback`] will be invoked and given the opportunity /// to provide a fallback. /// /// The link type (that's "link" or "image") depends on the usage site, and /// is provided by the caller of this function. /// This function returns a new one because, if it has to invoke a callback /// to find the information, the link type is [mapped to an unknown type]. /// /// [mapped to an unknown type]: crate::LinkType::to_unknown /// [`link_ref_expansion_limit`]: Self::link_ref_expansion_limit /// [`broken_link_callback`]: Self::broken_link_callback fn fetch_link_type_url_title( &mut self, link_label: CowStr<'input>, span: Range, link_type: LinkType ) -> Option<(LinkType, CowStr<'input>, CowStr<'input>)> { if self.link_ref_expansion_limit <= 0 { return None; } let (link_type, url, title) = self .allocs .refdefs .get(link_label.as_ref()) .map(|matching_def| { // found a matching definition! let title = matching_def .title .as_ref() .cloned() .unwrap_or_else(|| "".into()); let url = matching_def.dest.clone(); (link_type, url, title) }) .or_else(|| { match self.broken_link_callback.as_mut() { Some(callback) => { // Construct a BrokenLink struct, which will be passed to the callback let broken_link = BrokenLink { span, link_type, reference: link_label, }; callback.handle_broken_link(broken_link).map( |(url, title)| { (link_type.to_unknown(), url, title) }, ) } None => None, } })?; // Limit expansion from link references. // This isn't a problem for footnotes, because multiple references to the same one // reuse the same node, but links/images get their HREF/SRC copied. self.link_ref_expansion_limit = self.link_ref_expansion_limit .saturating_sub(url.len() + title.len()); Some((link_type, url, title)) } /// Handle inline markup. /// /// When the parser encounters any item indicating potential inline markup, all /// inline markup passes are run on the remainder of the chain. /// /// Note: there's some potential for optimization here, but that's future work. fn handle_inline(&mut self) { self.handle_inline_pass1(); self.handle_emphasis_and_hard_break(); } /// Handle inline HTML, code spans, and links. /// /// This function handles both inline HTML and code spans, because they have /// the same precedence. It also handles links, even though they have lower /// precedence, because the URL of links must not be processed. fn handle_inline_pass1(&mut self) { let mut code_delims = CodeDelims::new(); let mut cur = self.tree.cur(); let mut prev = None; let block_end = self.tree[self.tree.peek_up().unwrap()].item.end; let block_text = &self.text[..block_end]; while let Some(mut cur_ix) = cur { match self.tree[cur_ix].item.body { ItemBody::MaybeHtml => { let next = self.tree[cur_ix].next; let autolink = if let Some(next_ix) = next { scan_autolink(block_text, self.tree[next_ix].item.start) } else { None }; if let Some((ix, uri, link_type)) = autolink { let node = scan_nodes_to_ix(&self.tree, next, ix); let text_node = self.tree.create_node(Item { start: self.tree[cur_ix].item.start + 1, end: ix - 1, body: ItemBody::Text { backslash_escaped: false }, }); let link_ix = self.allocs .allocate_link(link_type, uri, "".into(), "".into()); self.tree[cur_ix].item.body = ItemBody::Link(link_ix); self.tree[cur_ix].item.end = ix; self.tree[cur_ix].next = node; self.tree[cur_ix].child = Some(text_node); prev = cur; cur = node; if let Some(node_ix) = cur { self.tree[node_ix].item.start = max(self.tree[node_ix].item.start, ix); } continue; } else { let inline_html = next.and_then(|next_ix| { self.scan_inline_html( block_text.as_bytes(), self.tree[next_ix].item.start, ) }); if let Some((span, ix)) = inline_html { let node = scan_nodes_to_ix(&self.tree, next, ix); self.tree[cur_ix].item.body = if !span.is_empty() { let converted_string = String::from_utf8(span).expect("invalid utf8"); ItemBody::OwnedHtml( self.allocs.allocate_cow(converted_string.into()), ) } else { ItemBody::InlineHtml }; self.tree[cur_ix].item.end = ix; self.tree[cur_ix].next = node; prev = cur; cur = node; if let Some(node_ix) = cur { self.tree[node_ix].item.start = max(self.tree[node_ix].item.start, ix); } continue; } } self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; } ItemBody::MaybeCode(mut search_count, preceded_by_backslash) => { if preceded_by_backslash { search_count -= 1; if search_count == 0 { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; prev = cur; cur = self.tree[cur_ix].next; continue; } } if code_delims.is_populated() { // we have previously scanned all codeblock delimiters, // so we can reuse that work if let Some(scan_ix) = code_delims.find(cur_ix, search_count) { self.make_code_span(cur_ix, scan_ix, preceded_by_backslash); } else { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; } } else { // we haven't previously scanned all codeblock delimiters, // so walk the AST let mut scan = if search_count > 0 { self.tree[cur_ix].next } else { None }; while let Some(scan_ix) = scan { if let ItemBody::MaybeCode(delim_count, _) = self.tree[scan_ix].item.body { if search_count == delim_count { self.make_code_span(cur_ix, scan_ix, preceded_by_backslash); code_delims.clear(); break; } else { code_delims.insert(delim_count, scan_ix); } } if self.tree[scan_ix].item.body.is_block() { // If this is a tight list, blocks and inlines might be // siblings. Inlines can't cross the boundary like that. scan = None; break; } scan = self.tree[scan_ix].next; } if scan.is_none() { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; } } } ItemBody::MaybeLinkOpen => { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; self.link_stack.push(LinkStackEl { node: cur_ix, ty: LinkStackTy::Link, }); } ItemBody::MaybeImage => { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; self.link_stack.push(LinkStackEl { node: cur_ix, ty: LinkStackTy::Image, }); } ItemBody::MaybeLinkClose(could_be_ref) => { self.tree[cur_ix].item.body = ItemBody::Text { backslash_escaped: false }; if let Some(tos) = self.link_stack.pop() { if tos.ty == LinkStackTy::Disabled { continue; } let next = self.tree[cur_ix].next; if let Some((next_ix, url, title)) = self.scan_inline_link(block_text, self.tree[cur_ix].item.end, next) { let next_node = scan_nodes_to_ix(&self.tree, next, next_ix); if let Some(prev_ix) = prev { self.tree[prev_ix].next = None; } cur = Some(tos.node); cur_ix = tos.node; let link_ix = self.allocs .allocate_link(LinkType::Inline, url, title, "".into()); self.tree[cur_ix].item.body = if tos.ty == LinkStackTy::Image { ItemBody::Image(link_ix) } else { ItemBody::Link(link_ix) }; self.tree[cur_ix].child = self.tree[cur_ix].next; self.tree[cur_ix].next = next_node; self.tree[cur_ix].item.end = next_ix; if let Some(next_node_ix) = next_node { self.tree[next_node_ix].item.start = max(self.tree[next_node_ix].item.start, next_ix); } if tos.ty == LinkStackTy::Link { self.link_stack.disable_all_links(); } } else { // ok, so its not an inline link. maybe it is a reference // to a defined link? let scan_result = scan_reference( &self.tree, block_text, next, self.options.contains(Options::ENABLE_FOOTNOTES), self.options.has_gfm_footnotes(), ); let (node_after_link, link_type) = match scan_result { // [label][reference] RefScan::LinkLabel(_, end_ix) => { // Toggle reference viability of the last closing bracket, // so that we can skip it on future iterations in case // it fails in this one. In particular, we won't call // the broken link callback twice on one reference. let reference_close_node = if let Some(node) = scan_nodes_to_ix(&self.tree, next, end_ix - 1) { node } else { continue; }; self.tree[reference_close_node].item.body = ItemBody::MaybeLinkClose(false); let next_node = self.tree[reference_close_node].next; (next_node, LinkType::Reference) } // [reference][] RefScan::Collapsed(next_node) => { // This reference has already been tried, and it's not // valid. Skip it. if !could_be_ref { continue; } (next_node, LinkType::Collapsed) } // [shortcut] // // [shortcut]: /blah RefScan::Failed => { if !could_be_ref { continue; } (next, LinkType::Shortcut) } RefScan::UnexpectedFootnote => continue, }; // FIXME: references and labels are mixed in the naming of variables // below. Disambiguate! // (label, source_ix end) let label: Option<(ReferenceLabel<'input>, usize)> = match scan_result { RefScan::LinkLabel(l, end_ix) => { Some((ReferenceLabel::Link(l), end_ix)) } RefScan::Collapsed(..) | RefScan::Failed => { // No label? maybe it is a shortcut reference let label_start = self.tree[tos.node].item.end - 1; let label_end = self.tree[cur_ix].item.end; scan_link_label( &self.tree, &self.text[label_start..label_end], self.options.contains(Options::ENABLE_FOOTNOTES), self.options.has_gfm_footnotes(), ) .map(|(ix, label)| (label, label_start + ix)) .filter(|(_, end)| *end == label_end) } RefScan::UnexpectedFootnote => continue, }; let id = match &label { Some((ReferenceLabel::Link(l), _) | (ReferenceLabel::Footnote(l), _)) => l.clone(), None => "".into(), }; // see if it's a footnote reference if let Some((ReferenceLabel::Footnote(l), end)) = label { let footref = self.allocs.allocate_cow(l); if let Some(def) = self .allocs .footdefs .get_mut(self.allocs.cows[footref.0].to_owned()) { def.use_count += 1; } if !self.options.has_gfm_footnotes() || self.allocs.footdefs.contains(&self.allocs.cows[footref.0]) { // If this came from a MaybeImage, then the `!` prefix // isn't part of the footnote reference. let footnote_ix = if tos.ty == LinkStackTy::Image { self.tree[tos.node].next = Some(cur_ix); self.tree[tos.node].child = None; self.tree[tos.node].item.body = ItemBody::SynthesizeChar('!'); cur_ix } else { tos.node }; // use `next` instead of `node_after_link` because // node_after_link is calculated for a [collapsed][] link, // which footnotes don't support. self.tree[footnote_ix].next = next; self.tree[footnote_ix].child = None; self.tree[footnote_ix].item.body = ItemBody::FootnoteReference(footref); self.tree[footnote_ix].item.end = end; prev = Some(footnote_ix); cur = next; self.link_stack.clear(); continue; } } else if let Some((ReferenceLabel::Link(link_label), end)) = label { if let Some((def_link_type, url, title)) = self.fetch_link_type_url_title( link_label, (self.tree[tos.node].item.start)..end, link_type, ) { let link_ix = self.allocs.allocate_link(def_link_type, url, title, id); self.tree[tos.node].item.body = if tos.ty == LinkStackTy::Image { ItemBody::Image(link_ix) } else { ItemBody::Link(link_ix) }; let label_node = self.tree[tos.node].next; // lets do some tree surgery to add the link to the tree // 1st: skip the label node and close node self.tree[tos.node].next = node_after_link; // then, if it exists, add the label node as a child to the link node if label_node != cur { self.tree[tos.node].child = label_node; // finally: disconnect list of children if let Some(prev_ix) = prev { self.tree[prev_ix].next = None; } } self.tree[tos.node].item.end = end; // set up cur so next node will be node_after_link cur = Some(tos.node); cur_ix = tos.node; if tos.ty == LinkStackTy::Link { self.link_stack.disable_all_links(); } } } } } } _ => { // If this is a tight list, blocks and inlines might be // siblings. Inlines can't cross the boundary like that. if let Some(cur_ix) = cur { if self.tree[cur_ix].item.body.is_block() { self.link_stack.clear(); } } } } prev = cur; cur = self.tree[cur_ix].next; } self.link_stack.clear(); } fn handle_emphasis_and_hard_break(&mut self) { let mut prev = None; let mut prev_ix: TreeIndex; let mut cur = self.tree.cur(); let mut single_quote_open: Option = None; let mut double_quote_open: bool = false; while let Some(mut cur_ix) = cur { match self.tree[cur_ix].item.body { ItemBody::MaybeEmphasis(mut count, can_open, can_close) => { let run_length = count; let c = self.text.as_bytes()[self.tree[cur_ix].item.start]; let both = can_open && can_close; if can_close { while let Some(el) = self.inline_stack.find_match(&mut self.tree, c, run_length, both) { // have a match! if let Some(prev_ix) = prev { self.tree[prev_ix].next = None; } let match_count = min(count, el.count); // start, end are tree node indices let mut end = cur_ix - 1; let mut start = el.start + el.count; // work from the inside out while start > el.start + el.count - match_count { let inc = if start > el.start + el.count - match_count + 1 { 2 } else { 1 }; let ty = if c == b'~' { ItemBody::Strikethrough } else if inc == 2 { ItemBody::Strong } else { ItemBody::Emphasis }; let root = start - inc; end = end + inc; self.tree[root].item.body = ty; self.tree[root].item.end = self.tree[end].item.end; self.tree[root].child = Some(start); self.tree[root].next = None; start = root; } // set next for top most emph level prev_ix = el.start + el.count - match_count; prev = Some(prev_ix); cur = self.tree[cur_ix + match_count - 1].next; self.tree[prev_ix].next = cur; if el.count > match_count { self.inline_stack.push(InlineEl { start: el.start, count: el.count - match_count, run_length: el.run_length, c: el.c, both: el.both, }) } count -= match_count; if count > 0 { cur_ix = cur.unwrap(); } else { break; } } } if count > 0 { if can_open { self.inline_stack.push(InlineEl { start: cur_ix, run_length, count, c, both, }); } else { for i in 0..count { self.tree[cur_ix + i].item.body = ItemBody::Text { backslash_escaped: false }; } } prev_ix = cur_ix + count - 1; prev = Some(prev_ix); cur = self.tree[prev_ix].next; } } ItemBody::MaybeSmartQuote(c, can_open, can_close) => { self.tree[cur_ix].item.body = match c { b'\'' => { if let (Some(open_ix), true) = (single_quote_open, can_close) { self.tree[open_ix].item.body = ItemBody::SynthesizeChar('‘'); single_quote_open = None; } else if can_open { single_quote_open = Some(cur_ix); } ItemBody::SynthesizeChar('’') } _ /* double quote */ => { if can_close && double_quote_open { double_quote_open = false; ItemBody::SynthesizeChar('”') } else { if can_open && !double_quote_open { double_quote_open = true; } ItemBody::SynthesizeChar('“') } } }; prev = cur; cur = self.tree[cur_ix].next; } ItemBody::HardBreak(true) => { if self.tree[cur_ix].next.is_none() { self.tree[cur_ix].item.body = ItemBody::SynthesizeChar('\\'); } prev = cur; cur = self.tree[cur_ix].next; } _ => { prev = cur; // If this is a tight list, blocks and inlines might be // siblings. Inlines can't cross the boundary like that. if let Some(cur_ix) = cur { if self.tree[cur_ix].item.body.is_block() { self.inline_stack.pop_all(&mut self.tree); } } cur = self.tree[cur_ix].next; } } } self.inline_stack.pop_all(&mut self.tree); } /// Returns next byte index, url and title. fn scan_inline_link( &self, underlying: &'input str, mut ix: usize, node: Option, ) -> Option<(usize, CowStr<'input>, CowStr<'input>)> { if scan_ch(&underlying.as_bytes()[ix..], b'(') == 0 { return None; } ix += 1; let scan_separator = |ix: &mut usize| { *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl); if let Some(bl) = scan_eol(&underlying.as_bytes()[*ix..]) { *ix += bl; let mut line_start = LineStart::new(&underlying.as_bytes()[*ix..]); let _ = scan_containers( &self.tree, &mut line_start, self.options.has_gfm_footnotes(), ); *ix += line_start.bytes_scanned(); } *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl); }; scan_separator(&mut ix); let (dest_length, dest) = scan_link_dest(underlying, ix, LINK_MAX_NESTED_PARENS)?; let dest = unescape(dest, self.tree.is_in_table()); ix += dest_length; scan_separator(&mut ix); let title = if let Some((bytes_scanned, t)) = self.scan_link_title(underlying, ix, node) { ix += bytes_scanned; scan_separator(&mut ix); t } else { "".into() }; if scan_ch(&underlying.as_bytes()[ix..], b')') == 0 { return None; } ix += 1; Some((ix, dest, title)) } // returns (bytes scanned, title cow) fn scan_link_title( &self, text: &'input str, start_ix: usize, node: Option, ) -> Option<(usize, CowStr<'input>)> { let bytes = text.as_bytes(); let open = match bytes.get(start_ix) { Some(b @ b'\'') | Some(b @ b'\"') | Some(b @ b'(') => *b, _ => return None, }; let close = if open == b'(' { b')' } else { open }; let mut title = String::new(); let mut mark = start_ix + 1; let mut i = start_ix + 1; while i < bytes.len() { let c = bytes[i]; if c == close { let cow = if mark == 1 { (i - start_ix + 1, text[mark..i].into()) } else { title.push_str(&text[mark..i]); (i - start_ix + 1, title.into()) }; return Some(cow); } if c == open { return None; } if c == b'\n' || c == b'\r' { if let Some(node_ix) = scan_nodes_to_ix(&self.tree, node, i + 1) { if self.tree[node_ix].item.start > i { title.push_str(&text[mark..i]); title.push('\n'); i = self.tree[node_ix].item.start; mark = i; continue; } } } if c == b'&' { if let (n, Some(value)) = scan_entity(&bytes[i..]) { title.push_str(&text[mark..i]); title.push_str(&value); i += n; mark = i; continue; } } if self.tree.is_in_table() && c == b'\\' && i + 2 < bytes.len() && bytes[i + 1] == b'\\' && bytes[i + 2] == b'|' { // this runs if there are an even number of pipes in a table // if it's odd, then it gets parsed as normal title.push_str(&text[mark..i]); i += 2; mark = i; } if c == b'\\' && i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) { title.push_str(&text[mark..i]); i += 1; mark = i; } i += 1; } None } /// Make a code span. /// /// Both `open` and `close` are matching MaybeCode items. fn make_code_span(&mut self, open: TreeIndex, close: TreeIndex, preceding_backslash: bool) { let bytes = self.text.as_bytes(); let span_start = self.tree[open].item.end; let span_end = self.tree[close].item.start; let mut buf: Option = None; let mut start_ix = span_start; let mut ix = span_start; while ix < span_end { let c = bytes[ix]; if c == b'\r' || c == b'\n' { let buf = buf.get_or_insert_with(|| String::with_capacity(ix + 1 - span_start)); buf.push_str(&self.text[start_ix..ix]); buf.push(' '); ix += 1; let mut line_start = LineStart::new(&bytes[ix..]); let _ = scan_containers( &self.tree, &mut line_start, self.options.has_gfm_footnotes(), ); ix += line_start.bytes_scanned(); start_ix = ix; } else if c == b'\\' && bytes.get(ix + 1) == Some(&b'|') && self.tree.is_in_table() { let buf = buf.get_or_insert_with(|| String::with_capacity(ix + 1 - span_start)); buf.push_str(&self.text[start_ix..ix]); buf.push('|'); ix += 2; start_ix = ix; } else { ix += 1; } } let (opening, closing, all_spaces) = { let s = if let Some(buf) = &mut buf { buf.push_str(&self.text[start_ix..span_end]); &buf[..] } else { &self.text[span_start..span_end] }; ( s.as_bytes().first() == Some(&b' '), s.as_bytes().last() == Some(&b' '), s.bytes().all(|b| b == b' ') ) }; let cow: CowStr<'input> = if !all_spaces && opening && closing { if let Some(mut buf) = buf { buf.remove(0); buf.pop(); buf.into() } else { let lo = span_start + 1; let hi = (span_end - 1).max(lo); self.text[lo..hi].into() } } else if let Some(buf) = buf { buf.into() } else { self.text[span_start..span_end].into() }; if preceding_backslash { self.tree[open].item.body = ItemBody::Text { backslash_escaped: true }; self.tree[open].item.end = self.tree[open].item.start + 1; self.tree[open].next = Some(close); self.tree[close].item.body = ItemBody::Code(self.allocs.allocate_cow(cow)); self.tree[close].item.start = self.tree[open].item.start + 1; } else { self.tree[open].item.body = ItemBody::Code(self.allocs.allocate_cow(cow)); self.tree[open].item.end = self.tree[close].item.end; self.tree[open].next = self.tree[close].next; } } /// On success, returns a buffer containing the inline html and byte offset. /// When no bytes were skipped, the buffer will be empty and the html can be /// represented as a subslice of the input string. fn scan_inline_html(&mut self, bytes: &[u8], ix: usize) -> Option<(Vec, usize)> { let c = *bytes.get(ix)?; if c == b'!' { Some(( vec![], scan_inline_html_comment(bytes, ix + 1, &mut self.html_scan_guard)?, )) } else if c == b'?' { Some(( vec![], scan_inline_html_processing(bytes, ix + 1, &mut self.html_scan_guard)?, )) } else { let (span, i) = scan_html_block_inner( // Subtract 1 to include the < character &bytes[(ix - 1)..], Some(&|bytes| { let mut line_start = LineStart::new(bytes); let _ = scan_containers( &self.tree, &mut line_start, self.options.has_gfm_footnotes(), ); line_start.bytes_scanned() }), )?; Some((span, i + ix - 1)) } } /// Consumes the event iterator and produces an iterator that produces /// `(Event, Range)` pairs, where the `Range` value maps to the corresponding /// range in the markdown source. pub fn into_offset_iter(self) -> OffsetIter<'input, F> { OffsetIter { inner: self } } } /// Returns number of containers scanned. pub(crate) fn scan_containers( tree: &Tree, line_start: &mut LineStart<'_>, gfm_footnotes: bool, ) -> usize { let mut i = 0; for &node_ix in tree.walk_spine() { match tree[node_ix].item.body { ItemBody::BlockQuote => { // `scan_blockquote_marker` saves & restores internally if !line_start.scan_blockquote_marker() { break; } } ItemBody::ListItem(indent) => { let save = line_start.clone(); if !line_start.scan_space(indent) && !line_start.is_at_eol() { *line_start = save; break; } } ItemBody::FootnoteDefinition(..) if gfm_footnotes => { let save = line_start.clone(); if !line_start.scan_space(4) && !line_start.is_at_eol() { *line_start = save; break; } } _ => (), } i += 1; } i } impl Tree { pub(crate) fn append_text(&mut self, start: usize, end: usize, backslash_escaped: bool) { if end > start { if let Some(ix) = self.cur() { if matches!(self[ix].item.body, ItemBody::Text { .. }) && self[ix].item.end == start { self[ix].item.end = end; return; } } self.append(Item { start, end, body: ItemBody::Text { backslash_escaped }, }); } } /// Returns true if the current node is inside a table. /// /// If `cur` is an ItemBody::Table, it would return false, /// but since the `TableRow` and `TableHead` and `TableCell` /// are children of the table, anything doing inline parsing /// doesn't need to care about that. pub(crate) fn is_in_table(&self) -> bool { fn might_be_in_table(item: &Item) -> bool { item.body.is_inline() || matches!(item.body, |ItemBody::TableHead| ItemBody::TableRow | ItemBody::TableCell) } for &ix in self.walk_spine().rev() { if matches!(self[ix].item.body, ItemBody::Table(_)) { return true; } if !might_be_in_table(&self[ix].item) { return false; } } false } } #[derive(Copy, Clone, Debug)] struct InlineEl { /// offset of tree node start: TreeIndex, /// number of delimiters available for matching count: usize, /// length of the run that these delimiters came from run_length: usize, /// b'*', b'_', or b'~' c: u8, /// can both open and close both: bool, } #[derive(Debug, Clone, Default)] struct InlineStack { stack: Vec, // Lower bounds for matching indices in the stack. For example // a strikethrough delimiter will never match with any element // in the stack with index smaller than // `lower_bounds[InlineStack::TILDES]`. lower_bounds: [usize; 9], } impl InlineStack { /// These are indices into the lower bounds array. /// Not both refers to the property that the delimiter can not both /// be opener as a closer. const UNDERSCORE_NOT_BOTH: usize = 0; const ASTERISK_NOT_BOTH: usize = 1; const ASTERISK_BASE: usize = 2; const TILDES: usize = 5; const UNDERSCORE_BASE: usize = 6; fn pop_all(&mut self, tree: &mut Tree) { for el in self.stack.drain(..) { for i in 0..el.count { tree[el.start + i].item.body = ItemBody::Text { backslash_escaped: false}; } } self.lower_bounds = [0; 9]; } fn get_lowerbound(&self, c: u8, count: usize, both: bool) -> usize { if c == b'_' { let mod3_lower = self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3]; if both { mod3_lower } else { min( mod3_lower, self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH], ) } } else if c == b'*' { let mod3_lower = self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3]; if both { mod3_lower } else { min( mod3_lower, self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH], ) } } else { self.lower_bounds[InlineStack::TILDES] } } fn set_lowerbound(&mut self, c: u8, count: usize, both: bool, new_bound: usize) { if c == b'_' { if both { self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3] = new_bound; } else { self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH] = new_bound; } } else if c == b'*' { self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3] = new_bound; if !both { self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH] = new_bound; } } else { self.lower_bounds[InlineStack::TILDES] = new_bound; } } fn truncate(&mut self, new_bound: usize) { self.stack.truncate(new_bound); for lower_bound in &mut self.lower_bounds { if *lower_bound > new_bound { *lower_bound = new_bound; } } } fn find_match( &mut self, tree: &mut Tree, c: u8, run_length: usize, both: bool, ) -> Option { let lowerbound = min(self.stack.len(), self.get_lowerbound(c, run_length, both)); let res = self.stack[lowerbound..] .iter() .cloned() .enumerate() .rfind(|(_, el)| { if c == b'~' && run_length != el.run_length { return false; } el.c == c && (!both && !el.both || (run_length + el.run_length) % 3 != 0 || run_length % 3 == 0) }); if let Some((matching_ix, matching_el)) = res { let matching_ix = matching_ix + lowerbound; for el in &self.stack[(matching_ix + 1)..] { for i in 0..el.count { tree[el.start + i].item.body = ItemBody::Text { backslash_escaped: false }; } } self.truncate(matching_ix); Some(matching_el) } else { self.set_lowerbound(c, run_length, both, self.stack.len()); None } } fn trim_lower_bound(&mut self, ix: usize) { self.lower_bounds[ix] = self.lower_bounds[ix].min(self.stack.len()); } fn push(&mut self, el: InlineEl) { if el.c == b'~' { self.trim_lower_bound(InlineStack::TILDES); } self.stack.push(el) } } #[derive(Debug, Clone)] enum RefScan<'a> { // label, source ix of label end LinkLabel(CowStr<'a>, usize), // contains next node index Collapsed(Option), UnexpectedFootnote, Failed, } /// Skips forward within a block to a node which spans (ends inclusive) the given /// index into the source. fn scan_nodes_to_ix( tree: &Tree, mut node: Option, ix: usize, ) -> Option { while let Some(node_ix) = node { if tree[node_ix].item.end <= ix { node = tree[node_ix].next; } else { break; } } node } /// Scans an inline link label, which cannot be interrupted. /// Returns number of bytes (including brackets) and label on success. fn scan_link_label<'text>( tree: &Tree, text: &'text str, allow_footnote_refs: bool, gfm_footnotes: bool, ) -> Option<(usize, ReferenceLabel<'text>)> { let bytes = text.as_bytes(); if bytes.len() < 2 || bytes[0] != b'[' { return None; } let linebreak_handler = |bytes: &[u8]| { let mut line_start = LineStart::new(bytes); let _ = scan_containers(tree, &mut line_start, gfm_footnotes); Some(line_start.bytes_scanned()) }; if allow_footnote_refs && b'^' == bytes[1] && bytes.get(2) != Some(&b']') { let linebreak_handler: &dyn Fn(&[u8]) -> Option = if gfm_footnotes { &|_| None } else { &linebreak_handler }; if let Some((byte_index, cow)) = scan_link_label_rest(&text[2..], linebreak_handler, tree.is_in_table()) { return Some((byte_index + 2, ReferenceLabel::Footnote(cow))); } } let (byte_index, cow) = scan_link_label_rest(&text[1..], &linebreak_handler, tree.is_in_table())?; Some((byte_index + 1, ReferenceLabel::Link(cow))) } fn scan_reference<'b>( tree: &Tree, text: &'b str, cur: Option, allow_footnote_refs: bool, gfm_footnotes: bool, ) -> RefScan<'b> { let cur_ix = match cur { None => return RefScan::Failed, Some(cur_ix) => cur_ix, }; let start = tree[cur_ix].item.start; let tail = &text.as_bytes()[start..]; if tail.starts_with(b"[]") { // TODO: this unwrap is sus and should be looked at closer let closing_node = tree[cur_ix].next.unwrap(); RefScan::Collapsed(tree[closing_node].next) } else { let label = scan_link_label(tree, &text[start..], allow_footnote_refs, gfm_footnotes); match label { Some((ix, ReferenceLabel::Link(label))) => RefScan::LinkLabel(label, start + ix), Some((_ix, ReferenceLabel::Footnote(_label))) => RefScan::UnexpectedFootnote, None => RefScan::Failed, } } } #[derive(Clone, Default)] struct LinkStack { inner: Vec, disabled_ix: usize, } impl LinkStack { fn push(&mut self, el: LinkStackEl) { self.inner.push(el); } fn pop(&mut self) -> Option { let el = self.inner.pop(); self.disabled_ix = std::cmp::min(self.disabled_ix, self.inner.len()); el } fn clear(&mut self) { self.inner.clear(); self.disabled_ix = 0; } fn disable_all_links(&mut self) { for el in &mut self.inner[self.disabled_ix..] { if el.ty == LinkStackTy::Link { el.ty = LinkStackTy::Disabled; } } self.disabled_ix = self.inner.len(); } } #[derive(Clone, Debug)] struct LinkStackEl { node: TreeIndex, ty: LinkStackTy, } #[derive(PartialEq, Clone, Debug)] enum LinkStackTy { Link, Image, Disabled, } /// Contains the destination URL, title and source span of a reference definition. #[derive(Clone, Debug)] pub struct LinkDef<'a> { pub dest: CowStr<'a>, pub title: Option>, pub span: Range, } /// Contains the destination URL, title and source span of a reference definition. #[derive(Clone, Debug)] pub struct FootnoteDef { pub use_count: usize, } /// Tracks tree indices of code span delimiters of each length. It should prevent /// quadratic scanning behaviours by providing (amortized) constant time lookups. struct CodeDelims { inner: HashMap>, seen_first: bool, } impl CodeDelims { fn new() -> Self { Self { inner: Default::default(), seen_first: false, } } fn insert(&mut self, count: usize, ix: TreeIndex) { if self.seen_first { self.inner .entry(count) .or_default() .push_back(ix); } else { // Skip the first insert, since that delimiter will always // be an opener and not a closer. self.seen_first = true; } } fn is_populated(&self) -> bool { !self.inner.is_empty() } fn find(&mut self, open_ix: TreeIndex, count: usize) -> Option { while let Some(ix) = self.inner.get_mut(&count)?.pop_front() { if ix > open_ix { return Some(ix); } } None } fn clear(&mut self) { self.inner.clear(); self.seen_first = false; } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) struct LinkIndex(usize); #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) struct CowIndex(usize); #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) struct AlignmentIndex(usize); #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) struct HeadingIndex(NonZeroUsize); #[derive(Clone)] pub(crate) struct Allocations<'a> { pub refdefs: RefDefs<'a>, pub footdefs: FootnoteDefs<'a>, links: Vec<(LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>)>, cows: Vec>, alignments: Vec>, headings: Vec>, } /// Used by the heading attributes extension. #[derive(Clone)] pub(crate) struct HeadingAttributes<'a> { pub id: Option>, pub classes: Vec>, pub attrs: Vec<(CowStr<'a>, Option>)>, } /// Keeps track of the reference definitions defined in the document. #[derive(Clone, Default, Debug)] pub struct RefDefs<'input>(pub(crate) HashMap, LinkDef<'input>>); /// Keeps track of the footnote definitions defined in the document. #[derive(Clone, Default, Debug)] pub struct FootnoteDefs<'input>(pub(crate) HashMap, FootnoteDef>); impl<'input, 'b, 's> RefDefs<'input> where 's: 'b, { /// Performs a lookup on reference label using unicode case folding. pub fn get(&'s self, key: &'b str) -> Option<&'b LinkDef<'input>> { self.0.get(&UniCase::new(key.into())) } /// Provides an iterator over all the document's reference definitions. pub fn iter(&'s self) -> impl Iterator)> { self.0.iter().map(|(k, v)| (k.as_ref(), v)) } } impl<'input, 'b, 's> FootnoteDefs<'input> where 's: 'b, { /// Performs a lookup on reference label using unicode case folding. pub fn contains(&'s self, key: &'b str) -> bool { self.0.contains_key(&UniCase::new(key.into())) } /// Performs a lookup on reference label using unicode case folding. pub fn get_mut(&'s mut self, key: CowStr<'input>) -> Option<&'s mut FootnoteDef> { self.0.get_mut(&UniCase::new(key)) } } impl<'a> Allocations<'a> { pub fn new() -> Self { Self { refdefs: RefDefs::default(), footdefs: FootnoteDefs::default(), links: Vec::with_capacity(128), cows: Vec::new(), alignments: Vec::new(), headings: Vec::new(), } } pub fn allocate_cow(&mut self, cow: CowStr<'a>) -> CowIndex { let ix = self.cows.len(); self.cows.push(cow); CowIndex(ix) } pub fn allocate_link( &mut self, ty: LinkType, url: CowStr<'a>, title: CowStr<'a>, id: CowStr<'a>, ) -> LinkIndex { let ix = self.links.len(); self.links.push((ty, url, title, id)); LinkIndex(ix) } pub fn allocate_alignment(&mut self, alignment: Vec) -> AlignmentIndex { let ix = self.alignments.len(); self.alignments.push(alignment); AlignmentIndex(ix) } pub fn allocate_heading(&mut self, attrs: HeadingAttributes<'a>) -> HeadingIndex { let ix = self.headings.len(); self.headings.push(attrs); // This won't panic. `self.headings.len()` can't be `usize::MAX` since // such a long Vec cannot fit in memory. let ix_nonzero = NonZeroUsize::new(ix.wrapping_add(1)).expect("too many headings"); HeadingIndex(ix_nonzero) } pub fn take_cow(&mut self, ix: CowIndex) -> CowStr<'a> { std::mem::replace(&mut self.cows[ix.0], "".into()) } pub fn take_link(&mut self, ix: LinkIndex) -> (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>) { let default_link = (LinkType::ShortcutUnknown, "".into(), "".into(), "".into()); std::mem::replace(&mut self.links[ix.0], default_link) } pub fn take_alignment(&mut self, ix: AlignmentIndex) -> Vec { std::mem::take(&mut self.alignments[ix.0]) } } impl<'a> Index for Allocations<'a> { type Output = CowStr<'a>; fn index(&self, ix: CowIndex) -> &Self::Output { self.cows.index(ix.0) } } impl<'a> Index for Allocations<'a> { type Output = (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>); fn index(&self, ix: LinkIndex) -> &Self::Output { self.links.index(ix.0) } } impl<'a> Index for Allocations<'a> { type Output = Vec; fn index(&self, ix: AlignmentIndex) -> &Self::Output { self.alignments.index(ix.0) } } impl<'a> Index for Allocations<'a> { type Output = HeadingAttributes<'a>; fn index(&self, ix: HeadingIndex) -> &Self::Output { self.headings.index(ix.0.get() - 1) } } /// A struct containing information on the reachability of certain inline HTML /// elements. In particular, for cdata elements (` { fn handle_broken_link( &mut self, link: BrokenLink<'input>, ) -> Option<(CowStr<'input>, CowStr<'input>)>; } impl<'input, T> BrokenLinkCallback<'input> for T where T: FnMut(BrokenLink<'input>) -> Option<(CowStr<'input>, CowStr<'input>)>, { fn handle_broken_link( &mut self, link: BrokenLink<'input>, ) -> Option<(CowStr<'input>, CowStr<'input>)> { self(link) } } impl<'input> BrokenLinkCallback<'input> for Box> { fn handle_broken_link( &mut self, link: BrokenLink<'input>, ) -> Option<(CowStr<'input>, CowStr<'input>)> { (**self).handle_broken_link(link) } } /// Broken link callback that does nothing. #[derive(Debug)] pub struct DefaultBrokenLinkCallback; impl<'input> BrokenLinkCallback<'input> for DefaultBrokenLinkCallback { fn handle_broken_link( &mut self, _link: BrokenLink<'input>, ) -> Option<(CowStr<'input>, CowStr<'input>)> { None } } /// Markdown event and source range iterator. /// /// Generates tuples where the first element is the markdown event and the second /// is a the corresponding range in the source string. /// /// Constructed from a `Parser` using its /// [`into_offset_iter`](struct.Parser.html#method.into_offset_iter) method. #[derive(Debug)] pub struct OffsetIter<'a, F> { inner: Parser<'a, F>, } impl<'a, F: BrokenLinkCallback<'a>> OffsetIter<'a, F> { /// Returns a reference to the internal reference definition tracker. pub fn reference_definitions(&self) -> &RefDefs<'_> { self.inner.reference_definitions() } } impl<'a, F: BrokenLinkCallback<'a>> Iterator for OffsetIter<'a, F> { type Item = (Event<'a>, Range); fn next(&mut self) -> Option { match self.inner.tree.cur() { None => { let ix = self.inner.tree.pop()?; let tag_end = body_to_tag_end(&self.inner.tree[ix].item.body); self.inner.tree.next_sibling(ix); let span = self.inner.tree[ix].item.start..self.inner.tree[ix].item.end; debug_assert!(span.start <= span.end); Some((Event::End(tag_end), span)) } Some(cur_ix) => { if self.inner.tree[cur_ix].item.body.is_inline() { self.inner.handle_inline(); } let node = self.inner.tree[cur_ix]; let item = node.item; let event = item_to_event(item, self.inner.text, &mut self.inner.allocs); if let Event::Start(..) = event { self.inner.tree.push(); } else { self.inner.tree.next_sibling(cur_ix); } debug_assert!(item.start <= item.end); Some((event, item.start..item.end)) } } } } fn body_to_tag_end(body: &ItemBody) -> TagEnd { match *body { ItemBody::Paragraph => TagEnd::Paragraph, ItemBody::Emphasis => TagEnd::Emphasis, ItemBody::Strong => TagEnd::Strong, ItemBody::Strikethrough => TagEnd::Strikethrough, ItemBody::Link(..) => TagEnd::Link, ItemBody::Image(..) => TagEnd::Image, ItemBody::Heading(level, _) => TagEnd::Heading(level), ItemBody::IndentCodeBlock | ItemBody::FencedCodeBlock(..) => TagEnd::CodeBlock, ItemBody::BlockQuote => TagEnd::BlockQuote, ItemBody::HtmlBlock => TagEnd::HtmlBlock, ItemBody::List(_, c, _) => { let is_ordered = c == b'.' || c == b')'; TagEnd::List(is_ordered) } ItemBody::ListItem(_) => TagEnd::Item, ItemBody::TableHead => TagEnd::TableHead, ItemBody::TableCell => TagEnd::TableCell, ItemBody::TableRow => TagEnd::TableRow, ItemBody::Table(..) => TagEnd::Table, ItemBody::FootnoteDefinition(..) => TagEnd::FootnoteDefinition, ItemBody::MetadataBlock(kind) => TagEnd::MetadataBlock(kind), _ => panic!("unexpected item body {:?}", body), } } fn item_to_event<'a>(item: Item, text: &'a str, allocs: &mut Allocations<'a>) -> Event<'a> { let tag = match item.body { ItemBody::Text { .. } => return Event::Text(text[item.start..item.end].into()), ItemBody::Code(cow_ix) => return Event::Code(allocs.take_cow(cow_ix)), ItemBody::SynthesizeText(cow_ix) => return Event::Text(allocs.take_cow(cow_ix)), ItemBody::SynthesizeChar(c) => return Event::Text(c.into()), ItemBody::HtmlBlock => Tag::HtmlBlock, ItemBody::Html => return Event::Html(text[item.start..item.end].into()), ItemBody::InlineHtml => return Event::InlineHtml(text[item.start..item.end].into()), ItemBody::OwnedHtml(cow_ix) => return Event::Html(allocs.take_cow(cow_ix)), ItemBody::SoftBreak => return Event::SoftBreak, ItemBody::HardBreak(_) => return Event::HardBreak, ItemBody::FootnoteReference(cow_ix) => { return Event::FootnoteReference(allocs.take_cow(cow_ix)) } ItemBody::TaskListMarker(checked) => return Event::TaskListMarker(checked), ItemBody::Rule => return Event::Rule, ItemBody::Paragraph => Tag::Paragraph, ItemBody::Emphasis => Tag::Emphasis, ItemBody::Strong => Tag::Strong, ItemBody::Strikethrough => Tag::Strikethrough, ItemBody::Link(link_ix) => { let (link_type, dest_url, title, id) = allocs.take_link(link_ix); Tag::Link { link_type, dest_url, title, id, } } ItemBody::Image(link_ix) => { let (link_type, dest_url, title, id) = allocs.take_link(link_ix); Tag::Image { link_type, dest_url, title, id, } } ItemBody::Heading(level, Some(heading_ix)) => { let HeadingAttributes { id, classes, attrs } = allocs.index(heading_ix); Tag::Heading { level, id: id.clone(), classes: classes.clone(), attrs: attrs.clone(), } } ItemBody::Heading(level, None) => Tag::Heading { level, id: None, classes: Vec::new(), attrs: Vec::new(), }, ItemBody::FencedCodeBlock(cow_ix) => { Tag::CodeBlock(CodeBlockKind::Fenced(allocs.take_cow(cow_ix))) } ItemBody::IndentCodeBlock => Tag::CodeBlock(CodeBlockKind::Indented), ItemBody::BlockQuote => Tag::BlockQuote, ItemBody::List(_, c, listitem_start) => { if c == b'.' || c == b')' { Tag::List(Some(listitem_start)) } else { Tag::List(None) } } ItemBody::ListItem(_) => Tag::Item, ItemBody::TableHead => Tag::TableHead, ItemBody::TableCell => Tag::TableCell, ItemBody::TableRow => Tag::TableRow, ItemBody::Table(alignment_ix) => Tag::Table(allocs.take_alignment(alignment_ix)), ItemBody::FootnoteDefinition(cow_ix) => Tag::FootnoteDefinition(allocs.take_cow(cow_ix)), ItemBody::MetadataBlock(kind) => Tag::MetadataBlock(kind), _ => panic!("unexpected item body {:?}", item.body), }; Event::Start(tag) } impl<'a, F: BrokenLinkCallback<'a>> Iterator for Parser<'a, F> { type Item = Event<'a>; fn next(&mut self) -> Option> { match self.tree.cur() { None => { let ix = self.tree.pop()?; let tag_end = body_to_tag_end(&self.tree[ix].item.body); self.tree.next_sibling(ix); Some(Event::End(tag_end)) } Some(cur_ix) => { if self.tree[cur_ix].item.body.is_inline() { self.handle_inline(); } let node = self.tree[cur_ix]; let item = node.item; let event = item_to_event(item, self.text, &mut self.allocs); if let Event::Start(ref _tag) = event { self.tree.push(); } else { self.tree.next_sibling(cur_ix); } Some(event) } } } } impl<'a, F: BrokenLinkCallback<'a>> FusedIterator for Parser<'a, F> {} #[cfg(test)] mod test { use super::*; use crate::tree::Node; // TODO: move these tests to tests/html.rs? fn parser_with_extensions(text: &str) -> Parser<'_> { let mut opts = Options::empty(); opts.insert(Options::ENABLE_TABLES); opts.insert(Options::ENABLE_FOOTNOTES); opts.insert(Options::ENABLE_STRIKETHROUGH); opts.insert(Options::ENABLE_TASKLISTS); Parser::new_ext(text, opts) } #[test] #[cfg(target_pointer_width = "64")] fn node_size() { let node_size = std::mem::size_of::>(); assert_eq!(48, node_size); } #[test] #[cfg(target_pointer_width = "64")] fn body_size() { let body_size = std::mem::size_of::(); assert_eq!(16, body_size); } #[test] fn single_open_fish_bracket() { // dont crash assert_eq!(3, Parser::new("<").count()); } #[test] fn lone_hashtag() { // dont crash assert_eq!(2, Parser::new("#").count()); } #[test] fn lots_of_backslashes() { // dont crash Parser::new("\\\\\r\r").count(); Parser::new("\\\r\r\\.\\\\\r\r\\.\\").count(); } #[test] fn issue_320() { // dont crash parser_with_extensions(":\r\t> |\r:\r\t> |\r").count(); } #[test] fn issue_319() { // dont crash parser_with_extensions("|\r-]([^|\r-]([^").count(); parser_with_extensions("|\r\r=][^|\r\r=][^car").count(); } #[test] fn issue_303() { // dont crash parser_with_extensions("[^\r\ra]").count(); parser_with_extensions("\r\r]Z[^\x00\r\r]Z[^\x00").count(); } #[test] fn issue_313() { // dont crash parser_with_extensions("*]0[^\r\r*]0[^").count(); parser_with_extensions("[^\r> `][^\r> `][^\r> `][").count(); } #[test] fn issue_311() { // dont crash parser_with_extensions("\\\u{0d}-\u{09}\\\u{0d}-\u{09}").count(); } #[test] fn issue_283() { let input = std::str::from_utf8(b"\xf0\x9b\xb2\x9f - \\\n> - ").count(); parser_with_extensions("- \n\n").count(); } #[test] fn issue_306() { // dont crash parser_with_extensions("*\r_<__*\r_<__*\r_<__*\r_<__").count(); } #[test] fn issue_305() { // dont crash parser_with_extensions("_6**6*_*").count(); } #[test] fn another_emphasis_panic() { parser_with_extensions("*__#_#__*").count(); } #[test] fn offset_iter() { let event_offsets: Vec<_> = Parser::new("*hello* world") .into_offset_iter() .map(|(_ev, range)| range) .collect(); let expected_offsets = vec![(0..13), (0..7), (1..6), (0..7), (7..13), (0..13)]; assert_eq!(expected_offsets, event_offsets); } #[test] fn reference_link_offsets() { let range = Parser::new("# H1\n[testing][Some reference]\n\n[Some reference]: https://github.com") .into_offset_iter() .filter_map(|(ev, range)| match ev { Event::Start( Tag::Link { link_type: LinkType::Reference, .. }, .., ) => Some(range), _ => None, }) .next() .unwrap(); assert_eq!(5..30, range); } #[test] fn footnote_offsets() { let range = parser_with_extensions("Testing this[^1] out.\n\n[^1]: Footnote.") .into_offset_iter() .filter_map(|(ev, range)| match ev { Event::FootnoteReference(..) => Some(range), _ => None, }) .next() .unwrap(); assert_eq!(12..16, range); } #[test] fn table_offset() { let markdown = "a\n\nTesting|This|Outtt\n--|:--:|--:\nSome Data|Other data|asdf"; let event_offset = parser_with_extensions(markdown) .into_offset_iter() .map(|(_ev, range)| range) .nth(3) .unwrap(); let expected_offset = 3..59; assert_eq!(expected_offset, event_offset); } #[test] fn table_cell_span() { let markdown = "a|b|c\n--|--|--\na| |c"; let event_offset = parser_with_extensions(markdown) .into_offset_iter() .filter_map(|(ev, span)| match ev { Event::Start(Tag::TableCell) => Some(span), _ => None, }) .nth(4) .unwrap(); let expected_offset_start = "a|b|c\n--|--|--\na|".len(); assert_eq!( expected_offset_start..(expected_offset_start + 2), event_offset ); } #[test] fn offset_iter_issue_378() { let event_offsets: Vec<_> = Parser::new("a [b](c) d") .into_offset_iter() .map(|(_ev, range)| range) .collect(); let expected_offsets = vec![(0..10), (0..2), (2..8), (3..4), (2..8), (8..10), (0..10)]; assert_eq!(expected_offsets, event_offsets); } #[test] fn offset_iter_issue_404() { let event_offsets: Vec<_> = Parser::new("###\n") .into_offset_iter() .map(|(_ev, range)| range) .collect(); let expected_offsets = vec![(0..4), (0..4)]; assert_eq!(expected_offsets, event_offsets); } // FIXME: add this one regression suite #[cfg(feature = "html")] #[test] fn link_def_at_eof() { let test_str = "[My site][world]\n\n[world]: https://vincentprouillet.com"; let expected = "

My site

\n"; let mut buf = String::new(); crate::html::push_html(&mut buf, Parser::new(test_str)); assert_eq!(expected, buf); } #[cfg(feature = "html")] #[test] fn no_footnote_refs_without_option() { let test_str = "a [^a]\n\n[^a]: yolo"; let expected = "

a ^a

\n"; let mut buf = String::new(); crate::html::push_html(&mut buf, Parser::new(test_str)); assert_eq!(expected, buf); } #[cfg(feature = "html")] #[test] fn ref_def_at_eof() { let test_str = "[test]:\\"; let expected = ""; let mut buf = String::new(); crate::html::push_html(&mut buf, Parser::new(test_str)); assert_eq!(expected, buf); } #[cfg(feature = "html")] #[test] fn ref_def_cr_lf() { let test_str = "[a]: /u\r\n\n[a]"; let expected = "

a

\n"; let mut buf = String::new(); crate::html::push_html(&mut buf, Parser::new(test_str)); assert_eq!(expected, buf); } #[cfg(feature = "html")] #[test] fn no_dest_refdef() { let test_str = "[a]:"; let expected = "

[a]:

\n"; let mut buf = String::new(); crate::html::push_html(&mut buf, Parser::new(test_str)); assert_eq!(expected, buf); } #[test] fn broken_links_called_only_once() { for &(markdown, expected) in &[ ("See also [`g()`][crate::g].", 1), ("See also [`g()`][crate::g][].", 1), ("[brokenlink1] some other node [brokenlink2]", 2), ] { let mut times_called = 0; let callback = &mut |_broken_link: BrokenLink| { times_called += 1; None }; let parser = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(callback)); for _ in parser {} assert_eq!(times_called, expected); } } #[test] fn simple_broken_link_callback() { let test_str = "This is a link w/o def: [hello][world]"; let mut callback = |broken_link: BrokenLink| { assert_eq!("world", broken_link.reference.as_ref()); assert_eq!(&test_str[broken_link.span], "[hello][world]"); let url = "YOLO".into(); let title = "SWAG".to_owned().into(); Some((url, title)) }; let parser = Parser::new_with_broken_link_callback(test_str, Options::empty(), Some(&mut callback)); let mut link_tag_count = 0; for (typ, url, title, id) in parser.filter_map(|event| match event { Event::Start(tag) => match tag { Tag::Link { link_type, dest_url, title, id, } => Some((link_type, dest_url, title, id)), _ => None, }, _ => None, }) { link_tag_count += 1; assert_eq!(typ, LinkType::ReferenceUnknown); assert_eq!(url.as_ref(), "YOLO"); assert_eq!(title.as_ref(), "SWAG"); assert_eq!(id.as_ref(), "world"); } assert!(link_tag_count > 0); } #[test] fn code_block_kind_check_fenced() { let parser = Parser::new("hello\n```test\ntadam\n```"); let mut found = 0; for (ev, _range) in parser.into_offset_iter() { match ev { Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(syntax))) => { assert_eq!(syntax.as_ref(), "test"); found += 1; } _ => {} } } assert_eq!(found, 1); } #[test] fn code_block_kind_check_indented() { let parser = Parser::new("hello\n\n ```test\n tadam\nhello"); let mut found = 0; for (ev, _range) in parser.into_offset_iter() { match ev { Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => { found += 1; } _ => {} } } assert_eq!(found, 1); } #[test] fn ref_defs() { let input = r###"[a B c]: http://example.com [another]: https://google.com text [final ONE]: http://wikipedia.org "###; let mut parser = Parser::new(input); assert!(parser.reference_definitions().get("a b c").is_some()); assert!(parser.reference_definitions().get("nope").is_none()); if let Some(_event) = parser.next() { // testing keys with shorter lifetimes than parser and its input let s = "final one".to_owned(); let link_def = parser.reference_definitions().get(&s).unwrap(); let span = &input[link_def.span.clone()]; assert_eq!(span, "[final ONE]: http://wikipedia.org"); } } #[test] fn common_lifetime_patterns_allowed<'b>() { let temporary_str = String::from("xyz"); // NOTE: this is a limitation of Rust, it doesn't allow putting lifetime parameters on the closure itself. // Hack it by attaching the lifetime to the test function instead. // TODO: why is the `'b` lifetime required at all? Changing it to `'_` breaks things :( let mut closure = |link: BrokenLink<'b>| Some(("#".into(), link.reference)); fn function(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)> { Some(("#".into(), link.reference)) } for _ in Parser::new_with_broken_link_callback( "static lifetime", Options::empty(), Some(&mut closure), ) {} /* This fails to compile. Because the closure can't say `for <'a> fn(BrokenLink<'a>) -> * CowStr<'a>` and has to use the enclosing `'b` lifetime parameter, `temporary_str` lives * shorter than `'b`. I think this is unlikely to occur in real life, and if it does, the * fix is simple: move it out to a function that allows annotating the lifetimes. */ //for _ in Parser::new_with_broken_link_callback(&temporary_str, Options::empty(), Some(&mut callback)) { //} for _ in Parser::new_with_broken_link_callback( "static lifetime", Options::empty(), Some(&mut function), ) {} for _ in Parser::new_with_broken_link_callback( &temporary_str, Options::empty(), Some(&mut function), ) {} } } pulldown-cmark-0.10.3/src/puncttable.rs000064400000000000000000001354021046102023000161470ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! CommonMark punctuation set based on spec and Unicode properties. // Autogenerated by mk_puncttable.py const PUNCT_MASKS_ASCII: [u16; 8] = [ 0x0000, // U+0000...U+000F 0x0000, // U+0010...U+001F 0xfffe, // U+0020...U+002F 0xfc00, // U+0030...U+003F 0x0001, // U+0040...U+004F 0xf800, // U+0050...U+005F 0x0001, // U+0060...U+006F 0x7800, // U+0070...U+007F ]; const PUNCT_TAB: [u16; 727] = [ 10, // U+00A0...U+00AF 11, // U+00B0...U+00BF 13, // U+00D0...U+00DF 15, // U+00F0...U+00FF 44, // U+02C0...U+02CF 45, // U+02D0...U+02DF 46, // U+02E0...U+02EF 47, // U+02F0...U+02FF 55, // U+0370...U+037F 56, // U+0380...U+038F 63, // U+03F0...U+03FF 72, // U+0480...U+048F 85, // U+0550...U+055F 88, // U+0580...U+058F 91, // U+05B0...U+05BF 92, // U+05C0...U+05CF 95, // U+05F0...U+05FF 96, // U+0600...U+060F 97, // U+0610...U+061F 102, // U+0660...U+066F 109, // U+06D0...U+06DF 110, // U+06E0...U+06EF 111, // U+06F0...U+06FF 112, // U+0700...U+070F 127, // U+07F0...U+07FF 131, // U+0830...U+083F 133, // U+0850...U+085F 136, // U+0880...U+088F 150, // U+0960...U+096F 151, // U+0970...U+097F 159, // U+09F0...U+09FF 167, // U+0A70...U+0A7F 175, // U+0AF0...U+0AFF 183, // U+0B70...U+0B7F 191, // U+0BF0...U+0BFF 199, // U+0C70...U+0C7F 200, // U+0C80...U+0C8F 212, // U+0D40...U+0D4F 215, // U+0D70...U+0D7F 223, // U+0DF0...U+0DFF 227, // U+0E30...U+0E3F 228, // U+0E40...U+0E4F 229, // U+0E50...U+0E5F 240, // U+0F00...U+0F0F 241, // U+0F10...U+0F1F 243, // U+0F30...U+0F3F 248, // U+0F80...U+0F8F 251, // U+0FB0...U+0FBF 252, // U+0FC0...U+0FCF 253, // U+0FD0...U+0FDF 260, // U+1040...U+104F 265, // U+1090...U+109F 271, // U+10F0...U+10FF 310, // U+1360...U+136F 313, // U+1390...U+139F 320, // U+1400...U+140F 358, // U+1660...U+166F 361, // U+1690...U+169F 366, // U+16E0...U+16EF 371, // U+1730...U+173F 381, // U+17D0...U+17DF 384, // U+1800...U+180F 404, // U+1940...U+194F 413, // U+19D0...U+19DF 414, // U+19E0...U+19EF 415, // U+19F0...U+19FF 417, // U+1A10...U+1A1F 426, // U+1AA0...U+1AAF 437, // U+1B50...U+1B5F 438, // U+1B60...U+1B6F 439, // U+1B70...U+1B7F 447, // U+1BF0...U+1BFF 451, // U+1C30...U+1C3F 455, // U+1C70...U+1C7F 460, // U+1CC0...U+1CCF 461, // U+1CD0...U+1CDF 507, // U+1FB0...U+1FBF 508, // U+1FC0...U+1FCF 509, // U+1FD0...U+1FDF 510, // U+1FE0...U+1FEF 511, // U+1FF0...U+1FFF 513, // U+2010...U+201F 514, // U+2020...U+202F 515, // U+2030...U+203F 516, // U+2040...U+204F 517, // U+2050...U+205F 519, // U+2070...U+207F 520, // U+2080...U+208F 522, // U+20A0...U+20AF 523, // U+20B0...U+20BF 524, // U+20C0...U+20CF 528, // U+2100...U+210F 529, // U+2110...U+211F 530, // U+2120...U+212F 531, // U+2130...U+213F 532, // U+2140...U+214F 536, // U+2180...U+218F 537, // U+2190...U+219F 538, // U+21A0...U+21AF 539, // U+21B0...U+21BF 540, // U+21C0...U+21CF 541, // U+21D0...U+21DF 542, // U+21E0...U+21EF 543, // U+21F0...U+21FF 544, // U+2200...U+220F 545, // U+2210...U+221F 546, // U+2220...U+222F 547, // U+2230...U+223F 548, // U+2240...U+224F 549, // U+2250...U+225F 550, // U+2260...U+226F 551, // U+2270...U+227F 552, // U+2280...U+228F 553, // U+2290...U+229F 554, // U+22A0...U+22AF 555, // U+22B0...U+22BF 556, // U+22C0...U+22CF 557, // U+22D0...U+22DF 558, // U+22E0...U+22EF 559, // U+22F0...U+22FF 560, // U+2300...U+230F 561, // U+2310...U+231F 562, // U+2320...U+232F 563, // U+2330...U+233F 564, // U+2340...U+234F 565, // U+2350...U+235F 566, // U+2360...U+236F 567, // U+2370...U+237F 568, // U+2380...U+238F 569, // U+2390...U+239F 570, // U+23A0...U+23AF 571, // U+23B0...U+23BF 572, // U+23C0...U+23CF 573, // U+23D0...U+23DF 574, // U+23E0...U+23EF 575, // U+23F0...U+23FF 576, // U+2400...U+240F 577, // U+2410...U+241F 578, // U+2420...U+242F 580, // U+2440...U+244F 585, // U+2490...U+249F 586, // U+24A0...U+24AF 587, // U+24B0...U+24BF 588, // U+24C0...U+24CF 589, // U+24D0...U+24DF 590, // U+24E0...U+24EF 592, // U+2500...U+250F 593, // U+2510...U+251F 594, // U+2520...U+252F 595, // U+2530...U+253F 596, // U+2540...U+254F 597, // U+2550...U+255F 598, // U+2560...U+256F 599, // U+2570...U+257F 600, // U+2580...U+258F 601, // U+2590...U+259F 602, // U+25A0...U+25AF 603, // U+25B0...U+25BF 604, // U+25C0...U+25CF 605, // U+25D0...U+25DF 606, // U+25E0...U+25EF 607, // U+25F0...U+25FF 608, // U+2600...U+260F 609, // U+2610...U+261F 610, // U+2620...U+262F 611, // U+2630...U+263F 612, // U+2640...U+264F 613, // U+2650...U+265F 614, // U+2660...U+266F 615, // U+2670...U+267F 616, // U+2680...U+268F 617, // U+2690...U+269F 618, // U+26A0...U+26AF 619, // U+26B0...U+26BF 620, // U+26C0...U+26CF 621, // U+26D0...U+26DF 622, // U+26E0...U+26EF 623, // U+26F0...U+26FF 624, // U+2700...U+270F 625, // U+2710...U+271F 626, // U+2720...U+272F 627, // U+2730...U+273F 628, // U+2740...U+274F 629, // U+2750...U+275F 630, // U+2760...U+276F 631, // U+2770...U+277F 633, // U+2790...U+279F 634, // U+27A0...U+27AF 635, // U+27B0...U+27BF 636, // U+27C0...U+27CF 637, // U+27D0...U+27DF 638, // U+27E0...U+27EF 639, // U+27F0...U+27FF 640, // U+2800...U+280F 641, // U+2810...U+281F 642, // U+2820...U+282F 643, // U+2830...U+283F 644, // U+2840...U+284F 645, // U+2850...U+285F 646, // U+2860...U+286F 647, // U+2870...U+287F 648, // U+2880...U+288F 649, // U+2890...U+289F 650, // U+28A0...U+28AF 651, // U+28B0...U+28BF 652, // U+28C0...U+28CF 653, // U+28D0...U+28DF 654, // U+28E0...U+28EF 655, // U+28F0...U+28FF 656, // U+2900...U+290F 657, // U+2910...U+291F 658, // U+2920...U+292F 659, // U+2930...U+293F 660, // U+2940...U+294F 661, // U+2950...U+295F 662, // U+2960...U+296F 663, // U+2970...U+297F 664, // U+2980...U+298F 665, // U+2990...U+299F 666, // U+29A0...U+29AF 667, // U+29B0...U+29BF 668, // U+29C0...U+29CF 669, // U+29D0...U+29DF 670, // U+29E0...U+29EF 671, // U+29F0...U+29FF 672, // U+2A00...U+2A0F 673, // U+2A10...U+2A1F 674, // U+2A20...U+2A2F 675, // U+2A30...U+2A3F 676, // U+2A40...U+2A4F 677, // U+2A50...U+2A5F 678, // U+2A60...U+2A6F 679, // U+2A70...U+2A7F 680, // U+2A80...U+2A8F 681, // U+2A90...U+2A9F 682, // U+2AA0...U+2AAF 683, // U+2AB0...U+2ABF 684, // U+2AC0...U+2ACF 685, // U+2AD0...U+2ADF 686, // U+2AE0...U+2AEF 687, // U+2AF0...U+2AFF 688, // U+2B00...U+2B0F 689, // U+2B10...U+2B1F 690, // U+2B20...U+2B2F 691, // U+2B30...U+2B3F 692, // U+2B40...U+2B4F 693, // U+2B50...U+2B5F 694, // U+2B60...U+2B6F 695, // U+2B70...U+2B7F 696, // U+2B80...U+2B8F 697, // U+2B90...U+2B9F 698, // U+2BA0...U+2BAF 699, // U+2BB0...U+2BBF 700, // U+2BC0...U+2BCF 701, // U+2BD0...U+2BDF 702, // U+2BE0...U+2BEF 703, // U+2BF0...U+2BFF 718, // U+2CE0...U+2CEF 719, // U+2CF0...U+2CFF 727, // U+2D70...U+2D7F 736, // U+2E00...U+2E0F 737, // U+2E10...U+2E1F 738, // U+2E20...U+2E2F 739, // U+2E30...U+2E3F 740, // U+2E40...U+2E4F 741, // U+2E50...U+2E5F 744, // U+2E80...U+2E8F 745, // U+2E90...U+2E9F 746, // U+2EA0...U+2EAF 747, // U+2EB0...U+2EBF 748, // U+2EC0...U+2ECF 749, // U+2ED0...U+2EDF 750, // U+2EE0...U+2EEF 751, // U+2EF0...U+2EFF 752, // U+2F00...U+2F0F 753, // U+2F10...U+2F1F 754, // U+2F20...U+2F2F 755, // U+2F30...U+2F3F 756, // U+2F40...U+2F4F 757, // U+2F50...U+2F5F 758, // U+2F60...U+2F6F 759, // U+2F70...U+2F7F 760, // U+2F80...U+2F8F 761, // U+2F90...U+2F9F 762, // U+2FA0...U+2FAF 763, // U+2FB0...U+2FBF 764, // U+2FC0...U+2FCF 765, // U+2FD0...U+2FDF 767, // U+2FF0...U+2FFF 768, // U+3000...U+300F 769, // U+3010...U+301F 770, // U+3020...U+302F 771, // U+3030...U+303F 777, // U+3090...U+309F 778, // U+30A0...U+30AF 783, // U+30F0...U+30FF 793, // U+3190...U+319F 796, // U+31C0...U+31CF 797, // U+31D0...U+31DF 798, // U+31E0...U+31EF 800, // U+3200...U+320F 801, // U+3210...U+321F 802, // U+3220...U+322F 803, // U+3230...U+323F 804, // U+3240...U+324F 805, // U+3250...U+325F 806, // U+3260...U+326F 807, // U+3270...U+327F 808, // U+3280...U+328F 809, // U+3290...U+329F 810, // U+32A0...U+32AF 811, // U+32B0...U+32BF 812, // U+32C0...U+32CF 813, // U+32D0...U+32DF 814, // U+32E0...U+32EF 815, // U+32F0...U+32FF 816, // U+3300...U+330F 817, // U+3310...U+331F 818, // U+3320...U+332F 819, // U+3330...U+333F 820, // U+3340...U+334F 821, // U+3350...U+335F 822, // U+3360...U+336F 823, // U+3370...U+337F 824, // U+3380...U+338F 825, // U+3390...U+339F 826, // U+33A0...U+33AF 827, // U+33B0...U+33BF 828, // U+33C0...U+33CF 829, // U+33D0...U+33DF 830, // U+33E0...U+33EF 831, // U+33F0...U+33FF 1244, // U+4DC0...U+4DCF 1245, // U+4DD0...U+4DDF 1246, // U+4DE0...U+4DEF 1247, // U+4DF0...U+4DFF 2633, // U+A490...U+A49F 2634, // U+A4A0...U+A4AF 2635, // U+A4B0...U+A4BF 2636, // U+A4C0...U+A4CF 2639, // U+A4F0...U+A4FF 2656, // U+A600...U+A60F 2663, // U+A670...U+A67F 2671, // U+A6F0...U+A6FF 2672, // U+A700...U+A70F 2673, // U+A710...U+A71F 2674, // U+A720...U+A72F 2680, // U+A780...U+A78F 2690, // U+A820...U+A82F 2691, // U+A830...U+A83F 2695, // U+A870...U+A87F 2700, // U+A8C0...U+A8CF 2703, // U+A8F0...U+A8FF 2706, // U+A920...U+A92F 2709, // U+A950...U+A95F 2716, // U+A9C0...U+A9CF 2717, // U+A9D0...U+A9DF 2725, // U+AA50...U+AA5F 2727, // U+AA70...U+AA7F 2733, // U+AAD0...U+AADF 2735, // U+AAF0...U+AAFF 2741, // U+AB50...U+AB5F 2742, // U+AB60...U+AB6F 2750, // U+ABE0...U+ABEF 4018, // U+FB20...U+FB2F 4027, // U+FBB0...U+FBBF 4028, // U+FBC0...U+FBCF 4051, // U+FD30...U+FD3F 4052, // U+FD40...U+FD4F 4060, // U+FDC0...U+FDCF 4063, // U+FDF0...U+FDFF 4065, // U+FE10...U+FE1F 4067, // U+FE30...U+FE3F 4068, // U+FE40...U+FE4F 4069, // U+FE50...U+FE5F 4070, // U+FE60...U+FE6F 4080, // U+FF00...U+FF0F 4081, // U+FF10...U+FF1F 4082, // U+FF20...U+FF2F 4083, // U+FF30...U+FF3F 4084, // U+FF40...U+FF4F 4085, // U+FF50...U+FF5F 4086, // U+FF60...U+FF6F 4094, // U+FFE0...U+FFEF 4095, // U+FFF0...U+FFFF 4112, // U+10100...U+1010F 4115, // U+10130...U+1013F 4119, // U+10170...U+1017F 4120, // U+10180...U+1018F 4121, // U+10190...U+1019F 4122, // U+101A0...U+101AF 4125, // U+101D0...U+101DF 4126, // U+101E0...U+101EF 4127, // U+101F0...U+101FF 4153, // U+10390...U+1039F 4157, // U+103D0...U+103DF 4182, // U+10560...U+1056F 4229, // U+10850...U+1085F 4231, // U+10870...U+1087F 4241, // U+10910...U+1091F 4243, // U+10930...U+1093F 4261, // U+10A50...U+10A5F 4263, // U+10A70...U+10A7F 4268, // U+10AC0...U+10ACF 4271, // U+10AF0...U+10AFF 4275, // U+10B30...U+10B3F 4281, // U+10B90...U+10B9F 4330, // U+10EA0...U+10EAF 4341, // U+10F50...U+10F5F 4344, // U+10F80...U+10F8F 4356, // U+11040...U+1104F 4363, // U+110B0...U+110BF 4364, // U+110C0...U+110CF 4372, // U+11140...U+1114F 4375, // U+11170...U+1117F 4380, // U+111C0...U+111CF 4381, // U+111D0...U+111DF 4387, // U+11230...U+1123F 4394, // U+112A0...U+112AF 4420, // U+11440...U+1144F 4421, // U+11450...U+1145F 4428, // U+114C0...U+114CF 4444, // U+115C0...U+115CF 4445, // U+115D0...U+115DF 4452, // U+11640...U+1164F 4454, // U+11660...U+1166F 4459, // U+116B0...U+116BF 4467, // U+11730...U+1173F 4483, // U+11830...U+1183F 4500, // U+11940...U+1194F 4510, // U+119E0...U+119EF 4515, // U+11A30...U+11A3F 4516, // U+11A40...U+11A4F 4521, // U+11A90...U+11A9F 4522, // U+11AA0...U+11AAF 4528, // U+11B00...U+11B0F 4548, // U+11C40...U+11C4F 4551, // U+11C70...U+11C7F 4591, // U+11EF0...U+11EFF 4596, // U+11F40...U+11F4F 4605, // U+11FD0...U+11FDF 4606, // U+11FE0...U+11FEF 4607, // U+11FF0...U+11FFF 4679, // U+12470...U+1247F 4863, // U+12FF0...U+12FFF 5798, // U+16A60...U+16A6F 5807, // U+16AF0...U+16AFF 5811, // U+16B30...U+16B3F 5812, // U+16B40...U+16B4F 5865, // U+16E90...U+16E9F 5886, // U+16FE0...U+16FEF 7113, // U+1BC90...U+1BC9F 7413, // U+1CF50...U+1CF5F 7414, // U+1CF60...U+1CF6F 7415, // U+1CF70...U+1CF7F 7416, // U+1CF80...U+1CF8F 7417, // U+1CF90...U+1CF9F 7418, // U+1CFA0...U+1CFAF 7419, // U+1CFB0...U+1CFBF 7420, // U+1CFC0...U+1CFCF 7424, // U+1D000...U+1D00F 7425, // U+1D010...U+1D01F 7426, // U+1D020...U+1D02F 7427, // U+1D030...U+1D03F 7428, // U+1D040...U+1D04F 7429, // U+1D050...U+1D05F 7430, // U+1D060...U+1D06F 7431, // U+1D070...U+1D07F 7432, // U+1D080...U+1D08F 7433, // U+1D090...U+1D09F 7434, // U+1D0A0...U+1D0AF 7435, // U+1D0B0...U+1D0BF 7436, // U+1D0C0...U+1D0CF 7437, // U+1D0D0...U+1D0DF 7438, // U+1D0E0...U+1D0EF 7439, // U+1D0F0...U+1D0FF 7440, // U+1D100...U+1D10F 7441, // U+1D110...U+1D11F 7442, // U+1D120...U+1D12F 7443, // U+1D130...U+1D13F 7444, // U+1D140...U+1D14F 7445, // U+1D150...U+1D15F 7446, // U+1D160...U+1D16F 7448, // U+1D180...U+1D18F 7449, // U+1D190...U+1D19F 7450, // U+1D1A0...U+1D1AF 7451, // U+1D1B0...U+1D1BF 7452, // U+1D1C0...U+1D1CF 7453, // U+1D1D0...U+1D1DF 7454, // U+1D1E0...U+1D1EF 7456, // U+1D200...U+1D20F 7457, // U+1D210...U+1D21F 7458, // U+1D220...U+1D22F 7459, // U+1D230...U+1D23F 7460, // U+1D240...U+1D24F 7472, // U+1D300...U+1D30F 7473, // U+1D310...U+1D31F 7474, // U+1D320...U+1D32F 7475, // U+1D330...U+1D33F 7476, // U+1D340...U+1D34F 7477, // U+1D350...U+1D35F 7532, // U+1D6C0...U+1D6CF 7533, // U+1D6D0...U+1D6DF 7535, // U+1D6F0...U+1D6FF 7537, // U+1D710...U+1D71F 7539, // U+1D730...U+1D73F 7540, // U+1D740...U+1D74F 7542, // U+1D760...U+1D76F 7544, // U+1D780...U+1D78F 7546, // U+1D7A0...U+1D7AF 7548, // U+1D7C0...U+1D7CF 7552, // U+1D800...U+1D80F 7553, // U+1D810...U+1D81F 7554, // U+1D820...U+1D82F 7555, // U+1D830...U+1D83F 7556, // U+1D840...U+1D84F 7557, // U+1D850...U+1D85F 7558, // U+1D860...U+1D86F 7559, // U+1D870...U+1D87F 7560, // U+1D880...U+1D88F 7561, // U+1D890...U+1D89F 7562, // U+1D8A0...U+1D8AF 7563, // U+1D8B0...U+1D8BF 7564, // U+1D8C0...U+1D8CF 7565, // U+1D8D0...U+1D8DF 7566, // U+1D8E0...U+1D8EF 7567, // U+1D8F0...U+1D8FF 7568, // U+1D900...U+1D90F 7569, // U+1D910...U+1D91F 7570, // U+1D920...U+1D92F 7571, // U+1D930...U+1D93F 7572, // U+1D940...U+1D94F 7573, // U+1D950...U+1D95F 7574, // U+1D960...U+1D96F 7575, // U+1D970...U+1D97F 7576, // U+1D980...U+1D98F 7577, // U+1D990...U+1D99F 7578, // U+1D9A0...U+1D9AF 7579, // U+1D9B0...U+1D9BF 7580, // U+1D9C0...U+1D9CF 7581, // U+1D9D0...U+1D9DF 7582, // U+1D9E0...U+1D9EF 7583, // U+1D9F0...U+1D9FF 7587, // U+1DA30...U+1DA3F 7590, // U+1DA60...U+1DA6F 7591, // U+1DA70...U+1DA7F 7592, // U+1DA80...U+1DA8F 7700, // U+1E140...U+1E14F 7727, // U+1E2F0...U+1E2FF 7829, // U+1E950...U+1E95F 7882, // U+1ECA0...U+1ECAF 7883, // U+1ECB0...U+1ECBF 7890, // U+1ED20...U+1ED2F 7919, // U+1EEF0...U+1EEFF 7936, // U+1F000...U+1F00F 7937, // U+1F010...U+1F01F 7938, // U+1F020...U+1F02F 7939, // U+1F030...U+1F03F 7940, // U+1F040...U+1F04F 7941, // U+1F050...U+1F05F 7942, // U+1F060...U+1F06F 7943, // U+1F070...U+1F07F 7944, // U+1F080...U+1F08F 7945, // U+1F090...U+1F09F 7946, // U+1F0A0...U+1F0AF 7947, // U+1F0B0...U+1F0BF 7948, // U+1F0C0...U+1F0CF 7949, // U+1F0D0...U+1F0DF 7950, // U+1F0E0...U+1F0EF 7951, // U+1F0F0...U+1F0FF 7952, // U+1F100...U+1F10F 7953, // U+1F110...U+1F11F 7954, // U+1F120...U+1F12F 7955, // U+1F130...U+1F13F 7956, // U+1F140...U+1F14F 7957, // U+1F150...U+1F15F 7958, // U+1F160...U+1F16F 7959, // U+1F170...U+1F17F 7960, // U+1F180...U+1F18F 7961, // U+1F190...U+1F19F 7962, // U+1F1A0...U+1F1AF 7966, // U+1F1E0...U+1F1EF 7967, // U+1F1F0...U+1F1FF 7968, // U+1F200...U+1F20F 7969, // U+1F210...U+1F21F 7970, // U+1F220...U+1F22F 7971, // U+1F230...U+1F23F 7972, // U+1F240...U+1F24F 7973, // U+1F250...U+1F25F 7974, // U+1F260...U+1F26F 7984, // U+1F300...U+1F30F 7985, // U+1F310...U+1F31F 7986, // U+1F320...U+1F32F 7987, // U+1F330...U+1F33F 7988, // U+1F340...U+1F34F 7989, // U+1F350...U+1F35F 7990, // U+1F360...U+1F36F 7991, // U+1F370...U+1F37F 7992, // U+1F380...U+1F38F 7993, // U+1F390...U+1F39F 7994, // U+1F3A0...U+1F3AF 7995, // U+1F3B0...U+1F3BF 7996, // U+1F3C0...U+1F3CF 7997, // U+1F3D0...U+1F3DF 7998, // U+1F3E0...U+1F3EF 7999, // U+1F3F0...U+1F3FF 8000, // U+1F400...U+1F40F 8001, // U+1F410...U+1F41F 8002, // U+1F420...U+1F42F 8003, // U+1F430...U+1F43F 8004, // U+1F440...U+1F44F 8005, // U+1F450...U+1F45F 8006, // U+1F460...U+1F46F 8007, // U+1F470...U+1F47F 8008, // U+1F480...U+1F48F 8009, // U+1F490...U+1F49F 8010, // U+1F4A0...U+1F4AF 8011, // U+1F4B0...U+1F4BF 8012, // U+1F4C0...U+1F4CF 8013, // U+1F4D0...U+1F4DF 8014, // U+1F4E0...U+1F4EF 8015, // U+1F4F0...U+1F4FF 8016, // U+1F500...U+1F50F 8017, // U+1F510...U+1F51F 8018, // U+1F520...U+1F52F 8019, // U+1F530...U+1F53F 8020, // U+1F540...U+1F54F 8021, // U+1F550...U+1F55F 8022, // U+1F560...U+1F56F 8023, // U+1F570...U+1F57F 8024, // U+1F580...U+1F58F 8025, // U+1F590...U+1F59F 8026, // U+1F5A0...U+1F5AF 8027, // U+1F5B0...U+1F5BF 8028, // U+1F5C0...U+1F5CF 8029, // U+1F5D0...U+1F5DF 8030, // U+1F5E0...U+1F5EF 8031, // U+1F5F0...U+1F5FF 8032, // U+1F600...U+1F60F 8033, // U+1F610...U+1F61F 8034, // U+1F620...U+1F62F 8035, // U+1F630...U+1F63F 8036, // U+1F640...U+1F64F 8037, // U+1F650...U+1F65F 8038, // U+1F660...U+1F66F 8039, // U+1F670...U+1F67F 8040, // U+1F680...U+1F68F 8041, // U+1F690...U+1F69F 8042, // U+1F6A0...U+1F6AF 8043, // U+1F6B0...U+1F6BF 8044, // U+1F6C0...U+1F6CF 8045, // U+1F6D0...U+1F6DF 8046, // U+1F6E0...U+1F6EF 8047, // U+1F6F0...U+1F6FF 8048, // U+1F700...U+1F70F 8049, // U+1F710...U+1F71F 8050, // U+1F720...U+1F72F 8051, // U+1F730...U+1F73F 8052, // U+1F740...U+1F74F 8053, // U+1F750...U+1F75F 8054, // U+1F760...U+1F76F 8055, // U+1F770...U+1F77F 8056, // U+1F780...U+1F78F 8057, // U+1F790...U+1F79F 8058, // U+1F7A0...U+1F7AF 8059, // U+1F7B0...U+1F7BF 8060, // U+1F7C0...U+1F7CF 8061, // U+1F7D0...U+1F7DF 8062, // U+1F7E0...U+1F7EF 8063, // U+1F7F0...U+1F7FF 8064, // U+1F800...U+1F80F 8065, // U+1F810...U+1F81F 8066, // U+1F820...U+1F82F 8067, // U+1F830...U+1F83F 8068, // U+1F840...U+1F84F 8069, // U+1F850...U+1F85F 8070, // U+1F860...U+1F86F 8071, // U+1F870...U+1F87F 8072, // U+1F880...U+1F88F 8073, // U+1F890...U+1F89F 8074, // U+1F8A0...U+1F8AF 8075, // U+1F8B0...U+1F8BF 8080, // U+1F900...U+1F90F 8081, // U+1F910...U+1F91F 8082, // U+1F920...U+1F92F 8083, // U+1F930...U+1F93F 8084, // U+1F940...U+1F94F 8085, // U+1F950...U+1F95F 8086, // U+1F960...U+1F96F 8087, // U+1F970...U+1F97F 8088, // U+1F980...U+1F98F 8089, // U+1F990...U+1F99F 8090, // U+1F9A0...U+1F9AF 8091, // U+1F9B0...U+1F9BF 8092, // U+1F9C0...U+1F9CF 8093, // U+1F9D0...U+1F9DF 8094, // U+1F9E0...U+1F9EF 8095, // U+1F9F0...U+1F9FF 8096, // U+1FA00...U+1FA0F 8097, // U+1FA10...U+1FA1F 8098, // U+1FA20...U+1FA2F 8099, // U+1FA30...U+1FA3F 8100, // U+1FA40...U+1FA4F 8101, // U+1FA50...U+1FA5F 8102, // U+1FA60...U+1FA6F 8103, // U+1FA70...U+1FA7F 8104, // U+1FA80...U+1FA8F 8105, // U+1FA90...U+1FA9F 8106, // U+1FAA0...U+1FAAF 8107, // U+1FAB0...U+1FABF 8108, // U+1FAC0...U+1FACF 8109, // U+1FAD0...U+1FADF 8110, // U+1FAE0...U+1FAEF 8111, // U+1FAF0...U+1FAFF 8112, // U+1FB00...U+1FB0F 8113, // U+1FB10...U+1FB1F 8114, // U+1FB20...U+1FB2F 8115, // U+1FB30...U+1FB3F 8116, // U+1FB40...U+1FB4F 8117, // U+1FB50...U+1FB5F 8118, // U+1FB60...U+1FB6F 8119, // U+1FB70...U+1FB7F 8120, // U+1FB80...U+1FB8F 8121, // U+1FB90...U+1FB9F 8122, // U+1FBA0...U+1FBAF 8123, // U+1FBB0...U+1FBBF 8124, // U+1FBC0...U+1FBCF ]; const PUNCT_MASKS: [u16; 727] = [ 0xdbfe, // U+00A0...U+00AF 0x89d3, // U+00B0...U+00BF 0x0080, // U+00D0...U+00DF 0x0080, // U+00F0...U+00FF 0x003c, // U+02C0...U+02CF 0xfffc, // U+02D0...U+02DF 0xafe0, // U+02E0...U+02EF 0xffff, // U+02F0...U+02FF 0x4020, // U+0370...U+037F 0x00b0, // U+0380...U+038F 0x0040, // U+03F0...U+03FF 0x0004, // U+0480...U+048F 0xfc00, // U+0550...U+055F 0xe600, // U+0580...U+058F 0x4000, // U+05B0...U+05BF 0x0049, // U+05C0...U+05CF 0x0018, // U+05F0...U+05FF 0xffc0, // U+0600...U+060F 0xe800, // U+0610...U+061F 0x3c00, // U+0660...U+066F 0x4010, // U+06D0...U+06DF 0x0200, // U+06E0...U+06EF 0x6000, // U+06F0...U+06FF 0x3fff, // U+0700...U+070F 0xc3c0, // U+07F0...U+07FF 0x7fff, // U+0830...U+083F 0x4000, // U+0850...U+085F 0x0100, // U+0880...U+088F 0x0030, // U+0960...U+096F 0x0001, // U+0970...U+097F 0x2c0c, // U+09F0...U+09FF 0x0040, // U+0A70...U+0A7F 0x0003, // U+0AF0...U+0AFF 0x0001, // U+0B70...U+0B7F 0x07f8, // U+0BF0...U+0BFF 0x8080, // U+0C70...U+0C7F 0x0010, // U+0C80...U+0C8F 0x8000, // U+0D40...U+0D4F 0x0200, // U+0D70...U+0D7F 0x0010, // U+0DF0...U+0DFF 0x8000, // U+0E30...U+0E3F 0x8000, // U+0E40...U+0E4F 0x0c00, // U+0E50...U+0E5F 0xfffe, // U+0F00...U+0F0F 0xfcff, // U+0F10...U+0F1F 0x3d50, // U+0F30...U+0F3F 0x0020, // U+0F80...U+0F8F 0xc000, // U+0FB0...U+0FBF 0xdfbf, // U+0FC0...U+0FCF 0x07ff, // U+0FD0...U+0FDF 0xfc00, // U+1040...U+104F 0xc000, // U+1090...U+109F 0x0800, // U+10F0...U+10FF 0x01ff, // U+1360...U+136F 0x03ff, // U+1390...U+139F 0x0001, // U+1400...U+140F 0x6000, // U+1660...U+166F 0x1800, // U+1690...U+169F 0x3800, // U+16E0...U+16EF 0x0060, // U+1730...U+173F 0x0f70, // U+17D0...U+17DF 0x07ff, // U+1800...U+180F 0x0031, // U+1940...U+194F 0xc000, // U+19D0...U+19DF 0xffff, // U+19E0...U+19EF 0xffff, // U+19F0...U+19FF 0xc000, // U+1A10...U+1A1F 0x3f7f, // U+1AA0...U+1AAF 0xfc00, // U+1B50...U+1B5F 0x07ff, // U+1B60...U+1B6F 0x7ff0, // U+1B70...U+1B7F 0xf000, // U+1BF0...U+1BFF 0xf800, // U+1C30...U+1C3F 0xc000, // U+1C70...U+1C7F 0x00ff, // U+1CC0...U+1CCF 0x0008, // U+1CD0...U+1CDF 0xa000, // U+1FB0...U+1FBF 0xe003, // U+1FC0...U+1FCF 0xe000, // U+1FD0...U+1FDF 0xe000, // U+1FE0...U+1FEF 0x6000, // U+1FF0...U+1FFF 0xffff, // U+2010...U+201F 0x00ff, // U+2020...U+202F 0xffff, // U+2030...U+203F 0xffff, // U+2040...U+204F 0x7fff, // U+2050...U+205F 0x7c00, // U+2070...U+207F 0x7c00, // U+2080...U+208F 0xffff, // U+20A0...U+20AF 0xffff, // U+20B0...U+20BF 0x0001, // U+20C0...U+20CF 0x037b, // U+2100...U+210F 0xc1d0, // U+2110...U+211F 0x42af, // U+2120...U+212F 0x0c00, // U+2130...U+213F 0xbc1f, // U+2140...U+214F 0x0c00, // U+2180...U+218F 0xffff, // U+2190...U+219F 0xffff, // U+21A0...U+21AF 0xffff, // U+21B0...U+21BF 0xffff, // U+21C0...U+21CF 0xffff, // U+21D0...U+21DF 0xffff, // U+21E0...U+21EF 0xffff, // U+21F0...U+21FF 0xffff, // U+2200...U+220F 0xffff, // U+2210...U+221F 0xffff, // U+2220...U+222F 0xffff, // U+2230...U+223F 0xffff, // U+2240...U+224F 0xffff, // U+2250...U+225F 0xffff, // U+2260...U+226F 0xffff, // U+2270...U+227F 0xffff, // U+2280...U+228F 0xffff, // U+2290...U+229F 0xffff, // U+22A0...U+22AF 0xffff, // U+22B0...U+22BF 0xffff, // U+22C0...U+22CF 0xffff, // U+22D0...U+22DF 0xffff, // U+22E0...U+22EF 0xffff, // U+22F0...U+22FF 0xffff, // U+2300...U+230F 0xffff, // U+2310...U+231F 0xffff, // U+2320...U+232F 0xffff, // U+2330...U+233F 0xffff, // U+2340...U+234F 0xffff, // U+2350...U+235F 0xffff, // U+2360...U+236F 0xffff, // U+2370...U+237F 0xffff, // U+2380...U+238F 0xffff, // U+2390...U+239F 0xffff, // U+23A0...U+23AF 0xffff, // U+23B0...U+23BF 0xffff, // U+23C0...U+23CF 0xffff, // U+23D0...U+23DF 0xffff, // U+23E0...U+23EF 0xffff, // U+23F0...U+23FF 0xffff, // U+2400...U+240F 0xffff, // U+2410...U+241F 0x007f, // U+2420...U+242F 0x07ff, // U+2440...U+244F 0xf000, // U+2490...U+249F 0xffff, // U+24A0...U+24AF 0xffff, // U+24B0...U+24BF 0xffff, // U+24C0...U+24CF 0xffff, // U+24D0...U+24DF 0x03ff, // U+24E0...U+24EF 0xffff, // U+2500...U+250F 0xffff, // U+2510...U+251F 0xffff, // U+2520...U+252F 0xffff, // U+2530...U+253F 0xffff, // U+2540...U+254F 0xffff, // U+2550...U+255F 0xffff, // U+2560...U+256F 0xffff, // U+2570...U+257F 0xffff, // U+2580...U+258F 0xffff, // U+2590...U+259F 0xffff, // U+25A0...U+25AF 0xffff, // U+25B0...U+25BF 0xffff, // U+25C0...U+25CF 0xffff, // U+25D0...U+25DF 0xffff, // U+25E0...U+25EF 0xffff, // U+25F0...U+25FF 0xffff, // U+2600...U+260F 0xffff, // U+2610...U+261F 0xffff, // U+2620...U+262F 0xffff, // U+2630...U+263F 0xffff, // U+2640...U+264F 0xffff, // U+2650...U+265F 0xffff, // U+2660...U+266F 0xffff, // U+2670...U+267F 0xffff, // U+2680...U+268F 0xffff, // U+2690...U+269F 0xffff, // U+26A0...U+26AF 0xffff, // U+26B0...U+26BF 0xffff, // U+26C0...U+26CF 0xffff, // U+26D0...U+26DF 0xffff, // U+26E0...U+26EF 0xffff, // U+26F0...U+26FF 0xffff, // U+2700...U+270F 0xffff, // U+2710...U+271F 0xffff, // U+2720...U+272F 0xffff, // U+2730...U+273F 0xffff, // U+2740...U+274F 0xffff, // U+2750...U+275F 0xffff, // U+2760...U+276F 0x003f, // U+2770...U+277F 0xfff0, // U+2790...U+279F 0xffff, // U+27A0...U+27AF 0xffff, // U+27B0...U+27BF 0xffff, // U+27C0...U+27CF 0xffff, // U+27D0...U+27DF 0xffff, // U+27E0...U+27EF 0xffff, // U+27F0...U+27FF 0xffff, // U+2800...U+280F 0xffff, // U+2810...U+281F 0xffff, // U+2820...U+282F 0xffff, // U+2830...U+283F 0xffff, // U+2840...U+284F 0xffff, // U+2850...U+285F 0xffff, // U+2860...U+286F 0xffff, // U+2870...U+287F 0xffff, // U+2880...U+288F 0xffff, // U+2890...U+289F 0xffff, // U+28A0...U+28AF 0xffff, // U+28B0...U+28BF 0xffff, // U+28C0...U+28CF 0xffff, // U+28D0...U+28DF 0xffff, // U+28E0...U+28EF 0xffff, // U+28F0...U+28FF 0xffff, // U+2900...U+290F 0xffff, // U+2910...U+291F 0xffff, // U+2920...U+292F 0xffff, // U+2930...U+293F 0xffff, // U+2940...U+294F 0xffff, // U+2950...U+295F 0xffff, // U+2960...U+296F 0xffff, // U+2970...U+297F 0xffff, // U+2980...U+298F 0xffff, // U+2990...U+299F 0xffff, // U+29A0...U+29AF 0xffff, // U+29B0...U+29BF 0xffff, // U+29C0...U+29CF 0xffff, // U+29D0...U+29DF 0xffff, // U+29E0...U+29EF 0xffff, // U+29F0...U+29FF 0xffff, // U+2A00...U+2A0F 0xffff, // U+2A10...U+2A1F 0xffff, // U+2A20...U+2A2F 0xffff, // U+2A30...U+2A3F 0xffff, // U+2A40...U+2A4F 0xffff, // U+2A50...U+2A5F 0xffff, // U+2A60...U+2A6F 0xffff, // U+2A70...U+2A7F 0xffff, // U+2A80...U+2A8F 0xffff, // U+2A90...U+2A9F 0xffff, // U+2AA0...U+2AAF 0xffff, // U+2AB0...U+2ABF 0xffff, // U+2AC0...U+2ACF 0xffff, // U+2AD0...U+2ADF 0xffff, // U+2AE0...U+2AEF 0xffff, // U+2AF0...U+2AFF 0xffff, // U+2B00...U+2B0F 0xffff, // U+2B10...U+2B1F 0xffff, // U+2B20...U+2B2F 0xffff, // U+2B30...U+2B3F 0xffff, // U+2B40...U+2B4F 0xffff, // U+2B50...U+2B5F 0xffff, // U+2B60...U+2B6F 0xffcf, // U+2B70...U+2B7F 0xffff, // U+2B80...U+2B8F 0xffbf, // U+2B90...U+2B9F 0xffff, // U+2BA0...U+2BAF 0xffff, // U+2BB0...U+2BBF 0xffff, // U+2BC0...U+2BCF 0xffff, // U+2BD0...U+2BDF 0xffff, // U+2BE0...U+2BEF 0xffff, // U+2BF0...U+2BFF 0x07e0, // U+2CE0...U+2CEF 0xde00, // U+2CF0...U+2CFF 0x0001, // U+2D70...U+2D7F 0xffff, // U+2E00...U+2E0F 0xffff, // U+2E10...U+2E1F 0x7fff, // U+2E20...U+2E2F 0xffff, // U+2E30...U+2E3F 0xffff, // U+2E40...U+2E4F 0x3fff, // U+2E50...U+2E5F 0xffff, // U+2E80...U+2E8F 0xfbff, // U+2E90...U+2E9F 0xffff, // U+2EA0...U+2EAF 0xffff, // U+2EB0...U+2EBF 0xffff, // U+2EC0...U+2ECF 0xffff, // U+2ED0...U+2EDF 0xffff, // U+2EE0...U+2EEF 0x000f, // U+2EF0...U+2EFF 0xffff, // U+2F00...U+2F0F 0xffff, // U+2F10...U+2F1F 0xffff, // U+2F20...U+2F2F 0xffff, // U+2F30...U+2F3F 0xffff, // U+2F40...U+2F4F 0xffff, // U+2F50...U+2F5F 0xffff, // U+2F60...U+2F6F 0xffff, // U+2F70...U+2F7F 0xffff, // U+2F80...U+2F8F 0xffff, // U+2F90...U+2F9F 0xffff, // U+2FA0...U+2FAF 0xffff, // U+2FB0...U+2FBF 0xffff, // U+2FC0...U+2FCF 0x003f, // U+2FD0...U+2FDF 0xffff, // U+2FF0...U+2FFF 0xff1e, // U+3000...U+300F 0xffff, // U+3010...U+301F 0x0001, // U+3020...U+302F 0xe0c1, // U+3030...U+303F 0x1800, // U+3090...U+309F 0x0001, // U+30A0...U+30AF 0x0800, // U+30F0...U+30FF 0xffc3, // U+3190...U+319F 0xffff, // U+31C0...U+31CF 0xffff, // U+31D0...U+31DF 0x800f, // U+31E0...U+31EF 0xffff, // U+3200...U+320F 0x7fff, // U+3210...U+321F 0xfc00, // U+3220...U+322F 0xffff, // U+3230...U+323F 0x00ff, // U+3240...U+324F 0x0001, // U+3250...U+325F 0xffff, // U+3260...U+326F 0xffff, // U+3270...U+327F 0xfc00, // U+3280...U+328F 0xffff, // U+3290...U+329F 0xffff, // U+32A0...U+32AF 0x0001, // U+32B0...U+32BF 0xffff, // U+32C0...U+32CF 0xffff, // U+32D0...U+32DF 0xffff, // U+32E0...U+32EF 0xffff, // U+32F0...U+32FF 0xffff, // U+3300...U+330F 0xffff, // U+3310...U+331F 0xffff, // U+3320...U+332F 0xffff, // U+3330...U+333F 0xffff, // U+3340...U+334F 0xffff, // U+3350...U+335F 0xffff, // U+3360...U+336F 0xffff, // U+3370...U+337F 0xffff, // U+3380...U+338F 0xffff, // U+3390...U+339F 0xffff, // U+33A0...U+33AF 0xffff, // U+33B0...U+33BF 0xffff, // U+33C0...U+33CF 0xffff, // U+33D0...U+33DF 0xffff, // U+33E0...U+33EF 0xffff, // U+33F0...U+33FF 0xffff, // U+4DC0...U+4DCF 0xffff, // U+4DD0...U+4DDF 0xffff, // U+4DE0...U+4DEF 0xffff, // U+4DF0...U+4DFF 0xffff, // U+A490...U+A49F 0xffff, // U+A4A0...U+A4AF 0xffff, // U+A4B0...U+A4BF 0x007f, // U+A4C0...U+A4CF 0xc000, // U+A4F0...U+A4FF 0xe000, // U+A600...U+A60F 0x4008, // U+A670...U+A67F 0x00fc, // U+A6F0...U+A6FF 0xffff, // U+A700...U+A70F 0x007f, // U+A710...U+A71F 0x0003, // U+A720...U+A72F 0x0600, // U+A780...U+A78F 0x0f00, // U+A820...U+A82F 0x03c0, // U+A830...U+A83F 0x00f0, // U+A870...U+A87F 0xc000, // U+A8C0...U+A8CF 0x1700, // U+A8F0...U+A8FF 0xc000, // U+A920...U+A92F 0x8000, // U+A950...U+A95F 0x3ffe, // U+A9C0...U+A9CF 0xc000, // U+A9D0...U+A9DF 0xf000, // U+AA50...U+AA5F 0x0380, // U+AA70...U+AA7F 0xc000, // U+AAD0...U+AADF 0x0003, // U+AAF0...U+AAFF 0x0800, // U+AB50...U+AB5F 0x0c00, // U+AB60...U+AB6F 0x0800, // U+ABE0...U+ABEF 0x0200, // U+FB20...U+FB2F 0xfffc, // U+FBB0...U+FBBF 0x0007, // U+FBC0...U+FBCF 0xc000, // U+FD30...U+FD3F 0xffff, // U+FD40...U+FD4F 0x8000, // U+FDC0...U+FDCF 0xf000, // U+FDF0...U+FDFF 0x03ff, // U+FE10...U+FE1F 0xffff, // U+FE30...U+FE3F 0xffff, // U+FE40...U+FE4F 0xfff7, // U+FE50...U+FE5F 0x0f7f, // U+FE60...U+FE6F 0xfffe, // U+FF00...U+FF0F 0xfc00, // U+FF10...U+FF1F 0x0001, // U+FF20...U+FF2F 0xf800, // U+FF30...U+FF3F 0x0001, // U+FF40...U+FF4F 0xf800, // U+FF50...U+FF5F 0x003f, // U+FF60...U+FF6F 0x7f7f, // U+FFE0...U+FFEF 0x3000, // U+FFF0...U+FFFF 0x0007, // U+10100...U+1010F 0xff80, // U+10130...U+1013F 0xfe00, // U+10170...U+1017F 0x73ff, // U+10180...U+1018F 0x1fff, // U+10190...U+1019F 0x0001, // U+101A0...U+101AF 0xffff, // U+101D0...U+101DF 0xffff, // U+101E0...U+101EF 0x1fff, // U+101F0...U+101FF 0x8000, // U+10390...U+1039F 0x0001, // U+103D0...U+103DF 0x8000, // U+10560...U+1056F 0x0080, // U+10850...U+1085F 0x0180, // U+10870...U+1087F 0x8000, // U+10910...U+1091F 0x8000, // U+10930...U+1093F 0x01ff, // U+10A50...U+10A5F 0x8000, // U+10A70...U+10A7F 0x0100, // U+10AC0...U+10ACF 0x007f, // U+10AF0...U+10AFF 0xfe00, // U+10B30...U+10B3F 0x1e00, // U+10B90...U+10B9F 0x2000, // U+10EA0...U+10EAF 0x03e0, // U+10F50...U+10F5F 0x03c0, // U+10F80...U+10F8F 0x3f80, // U+11040...U+1104F 0xd800, // U+110B0...U+110BF 0x0003, // U+110C0...U+110CF 0x000f, // U+11140...U+1114F 0x0030, // U+11170...U+1117F 0x21e0, // U+111C0...U+111CF 0xe800, // U+111D0...U+111DF 0x3f00, // U+11230...U+1123F 0x0200, // U+112A0...U+112AF 0xf800, // U+11440...U+1144F 0x2c00, // U+11450...U+1145F 0x0040, // U+114C0...U+114CF 0xfffe, // U+115C0...U+115CF 0x00ff, // U+115D0...U+115DF 0x000e, // U+11640...U+1164F 0x1fff, // U+11660...U+1166F 0x0200, // U+116B0...U+116BF 0xf000, // U+11730...U+1173F 0x0800, // U+11830...U+1183F 0x0070, // U+11940...U+1194F 0x0004, // U+119E0...U+119EF 0x8000, // U+11A30...U+11A3F 0x007f, // U+11A40...U+11A4F 0xdc00, // U+11A90...U+11A9F 0x0007, // U+11AA0...U+11AAF 0x03ff, // U+11B00...U+11B0F 0x003e, // U+11C40...U+11C4F 0x0003, // U+11C70...U+11C7F 0x0180, // U+11EF0...U+11EFF 0xfff8, // U+11F40...U+11F4F 0xffe0, // U+11FD0...U+11FDF 0xffff, // U+11FE0...U+11FEF 0x8003, // U+11FF0...U+11FFF 0x001f, // U+12470...U+1247F 0x0006, // U+12FF0...U+12FFF 0xc000, // U+16A60...U+16A6F 0x0020, // U+16AF0...U+16AFF 0xff80, // U+16B30...U+16B3F 0x0030, // U+16B40...U+16B4F 0x0780, // U+16E90...U+16E9F 0x0004, // U+16FE0...U+16FEF 0x9000, // U+1BC90...U+1BC9F 0xffff, // U+1CF50...U+1CF5F 0xffff, // U+1CF60...U+1CF6F 0xffff, // U+1CF70...U+1CF7F 0xffff, // U+1CF80...U+1CF8F 0xffff, // U+1CF90...U+1CF9F 0xffff, // U+1CFA0...U+1CFAF 0xffff, // U+1CFB0...U+1CFBF 0x000f, // U+1CFC0...U+1CFCF 0xffff, // U+1D000...U+1D00F 0xffff, // U+1D010...U+1D01F 0xffff, // U+1D020...U+1D02F 0xffff, // U+1D030...U+1D03F 0xffff, // U+1D040...U+1D04F 0xffff, // U+1D050...U+1D05F 0xffff, // U+1D060...U+1D06F 0xffff, // U+1D070...U+1D07F 0xffff, // U+1D080...U+1D08F 0xffff, // U+1D090...U+1D09F 0xffff, // U+1D0A0...U+1D0AF 0xffff, // U+1D0B0...U+1D0BF 0xffff, // U+1D0C0...U+1D0CF 0xffff, // U+1D0D0...U+1D0DF 0xffff, // U+1D0E0...U+1D0EF 0x003f, // U+1D0F0...U+1D0FF 0xffff, // U+1D100...U+1D10F 0xffff, // U+1D110...U+1D11F 0xfe7f, // U+1D120...U+1D12F 0xffff, // U+1D130...U+1D13F 0xffff, // U+1D140...U+1D14F 0xffff, // U+1D150...U+1D15F 0x1c1f, // U+1D160...U+1D16F 0xf018, // U+1D180...U+1D18F 0xffff, // U+1D190...U+1D19F 0xc3ff, // U+1D1A0...U+1D1AF 0xffff, // U+1D1B0...U+1D1BF 0xffff, // U+1D1C0...U+1D1CF 0xffff, // U+1D1D0...U+1D1DF 0x07ff, // U+1D1E0...U+1D1EF 0xffff, // U+1D200...U+1D20F 0xffff, // U+1D210...U+1D21F 0xffff, // U+1D220...U+1D22F 0xffff, // U+1D230...U+1D23F 0x0023, // U+1D240...U+1D24F 0xffff, // U+1D300...U+1D30F 0xffff, // U+1D310...U+1D31F 0xffff, // U+1D320...U+1D32F 0xffff, // U+1D330...U+1D33F 0xffff, // U+1D340...U+1D34F 0x007f, // U+1D350...U+1D35F 0x0002, // U+1D6C0...U+1D6CF 0x0800, // U+1D6D0...U+1D6DF 0x0800, // U+1D6F0...U+1D6FF 0x0020, // U+1D710...U+1D71F 0x0020, // U+1D730...U+1D73F 0x8000, // U+1D740...U+1D74F 0x8000, // U+1D760...U+1D76F 0x0200, // U+1D780...U+1D78F 0x0200, // U+1D7A0...U+1D7AF 0x0008, // U+1D7C0...U+1D7CF 0xffff, // U+1D800...U+1D80F 0xffff, // U+1D810...U+1D81F 0xffff, // U+1D820...U+1D82F 0xffff, // U+1D830...U+1D83F 0xffff, // U+1D840...U+1D84F 0xffff, // U+1D850...U+1D85F 0xffff, // U+1D860...U+1D86F 0xffff, // U+1D870...U+1D87F 0xffff, // U+1D880...U+1D88F 0xffff, // U+1D890...U+1D89F 0xffff, // U+1D8A0...U+1D8AF 0xffff, // U+1D8B0...U+1D8BF 0xffff, // U+1D8C0...U+1D8CF 0xffff, // U+1D8D0...U+1D8DF 0xffff, // U+1D8E0...U+1D8EF 0xffff, // U+1D8F0...U+1D8FF 0xffff, // U+1D900...U+1D90F 0xffff, // U+1D910...U+1D91F 0xffff, // U+1D920...U+1D92F 0xffff, // U+1D930...U+1D93F 0xffff, // U+1D940...U+1D94F 0xffff, // U+1D950...U+1D95F 0xffff, // U+1D960...U+1D96F 0xffff, // U+1D970...U+1D97F 0xffff, // U+1D980...U+1D98F 0xffff, // U+1D990...U+1D99F 0xffff, // U+1D9A0...U+1D9AF 0xffff, // U+1D9B0...U+1D9BF 0xffff, // U+1D9C0...U+1D9CF 0xffff, // U+1D9D0...U+1D9DF 0xffff, // U+1D9E0...U+1D9EF 0xffff, // U+1D9F0...U+1D9FF 0x0780, // U+1DA30...U+1DA3F 0xe000, // U+1DA60...U+1DA6F 0xffdf, // U+1DA70...U+1DA7F 0x0fef, // U+1DA80...U+1DA8F 0x8000, // U+1E140...U+1E14F 0x8000, // U+1E2F0...U+1E2FF 0xc000, // U+1E950...U+1E95F 0x1000, // U+1ECA0...U+1ECAF 0x0001, // U+1ECB0...U+1ECBF 0x4000, // U+1ED20...U+1ED2F 0x0003, // U+1EEF0...U+1EEFF 0xffff, // U+1F000...U+1F00F 0xffff, // U+1F010...U+1F01F 0x0fff, // U+1F020...U+1F02F 0xffff, // U+1F030...U+1F03F 0xffff, // U+1F040...U+1F04F 0xffff, // U+1F050...U+1F05F 0xffff, // U+1F060...U+1F06F 0xffff, // U+1F070...U+1F07F 0xffff, // U+1F080...U+1F08F 0x000f, // U+1F090...U+1F09F 0x7fff, // U+1F0A0...U+1F0AF 0xfffe, // U+1F0B0...U+1F0BF 0xfffe, // U+1F0C0...U+1F0CF 0xfffe, // U+1F0D0...U+1F0DF 0xffff, // U+1F0E0...U+1F0EF 0x003f, // U+1F0F0...U+1F0FF 0xe000, // U+1F100...U+1F10F 0xffff, // U+1F110...U+1F11F 0xffff, // U+1F120...U+1F12F 0xffff, // U+1F130...U+1F13F 0xffff, // U+1F140...U+1F14F 0xffff, // U+1F150...U+1F15F 0xffff, // U+1F160...U+1F16F 0xffff, // U+1F170...U+1F17F 0xffff, // U+1F180...U+1F18F 0xffff, // U+1F190...U+1F19F 0x3fff, // U+1F1A0...U+1F1AF 0xffc0, // U+1F1E0...U+1F1EF 0xffff, // U+1F1F0...U+1F1FF 0x0007, // U+1F200...U+1F20F 0xffff, // U+1F210...U+1F21F 0xffff, // U+1F220...U+1F22F 0x0fff, // U+1F230...U+1F23F 0x01ff, // U+1F240...U+1F24F 0x0003, // U+1F250...U+1F25F 0x003f, // U+1F260...U+1F26F 0xffff, // U+1F300...U+1F30F 0xffff, // U+1F310...U+1F31F 0xffff, // U+1F320...U+1F32F 0xffff, // U+1F330...U+1F33F 0xffff, // U+1F340...U+1F34F 0xffff, // U+1F350...U+1F35F 0xffff, // U+1F360...U+1F36F 0xffff, // U+1F370...U+1F37F 0xffff, // U+1F380...U+1F38F 0xffff, // U+1F390...U+1F39F 0xffff, // U+1F3A0...U+1F3AF 0xffff, // U+1F3B0...U+1F3BF 0xffff, // U+1F3C0...U+1F3CF 0xffff, // U+1F3D0...U+1F3DF 0xffff, // U+1F3E0...U+1F3EF 0xffff, // U+1F3F0...U+1F3FF 0xffff, // U+1F400...U+1F40F 0xffff, // U+1F410...U+1F41F 0xffff, // U+1F420...U+1F42F 0xffff, // U+1F430...U+1F43F 0xffff, // U+1F440...U+1F44F 0xffff, // U+1F450...U+1F45F 0xffff, // U+1F460...U+1F46F 0xffff, // U+1F470...U+1F47F 0xffff, // U+1F480...U+1F48F 0xffff, // U+1F490...U+1F49F 0xffff, // U+1F4A0...U+1F4AF 0xffff, // U+1F4B0...U+1F4BF 0xffff, // U+1F4C0...U+1F4CF 0xffff, // U+1F4D0...U+1F4DF 0xffff, // U+1F4E0...U+1F4EF 0xffff, // U+1F4F0...U+1F4FF 0xffff, // U+1F500...U+1F50F 0xffff, // U+1F510...U+1F51F 0xffff, // U+1F520...U+1F52F 0xffff, // U+1F530...U+1F53F 0xffff, // U+1F540...U+1F54F 0xffff, // U+1F550...U+1F55F 0xffff, // U+1F560...U+1F56F 0xffff, // U+1F570...U+1F57F 0xffff, // U+1F580...U+1F58F 0xffff, // U+1F590...U+1F59F 0xffff, // U+1F5A0...U+1F5AF 0xffff, // U+1F5B0...U+1F5BF 0xffff, // U+1F5C0...U+1F5CF 0xffff, // U+1F5D0...U+1F5DF 0xffff, // U+1F5E0...U+1F5EF 0xffff, // U+1F5F0...U+1F5FF 0xffff, // U+1F600...U+1F60F 0xffff, // U+1F610...U+1F61F 0xffff, // U+1F620...U+1F62F 0xffff, // U+1F630...U+1F63F 0xffff, // U+1F640...U+1F64F 0xffff, // U+1F650...U+1F65F 0xffff, // U+1F660...U+1F66F 0xffff, // U+1F670...U+1F67F 0xffff, // U+1F680...U+1F68F 0xffff, // U+1F690...U+1F69F 0xffff, // U+1F6A0...U+1F6AF 0xffff, // U+1F6B0...U+1F6BF 0xffff, // U+1F6C0...U+1F6CF 0xf0ff, // U+1F6D0...U+1F6DF 0x1fff, // U+1F6E0...U+1F6EF 0x1fff, // U+1F6F0...U+1F6FF 0xffff, // U+1F700...U+1F70F 0xffff, // U+1F710...U+1F71F 0xffff, // U+1F720...U+1F72F 0xffff, // U+1F730...U+1F73F 0xffff, // U+1F740...U+1F74F 0xffff, // U+1F750...U+1F75F 0xffff, // U+1F760...U+1F76F 0xf87f, // U+1F770...U+1F77F 0xffff, // U+1F780...U+1F78F 0xffff, // U+1F790...U+1F79F 0xffff, // U+1F7A0...U+1F7AF 0xffff, // U+1F7B0...U+1F7BF 0xffff, // U+1F7C0...U+1F7CF 0x03ff, // U+1F7D0...U+1F7DF 0x0fff, // U+1F7E0...U+1F7EF 0x0001, // U+1F7F0...U+1F7FF 0x0fff, // U+1F800...U+1F80F 0xffff, // U+1F810...U+1F81F 0xffff, // U+1F820...U+1F82F 0xffff, // U+1F830...U+1F83F 0x00ff, // U+1F840...U+1F84F 0x03ff, // U+1F850...U+1F85F 0xffff, // U+1F860...U+1F86F 0xffff, // U+1F870...U+1F87F 0x00ff, // U+1F880...U+1F88F 0xffff, // U+1F890...U+1F89F 0x3fff, // U+1F8A0...U+1F8AF 0x0003, // U+1F8B0...U+1F8BF 0xffff, // U+1F900...U+1F90F 0xffff, // U+1F910...U+1F91F 0xffff, // U+1F920...U+1F92F 0xffff, // U+1F930...U+1F93F 0xffff, // U+1F940...U+1F94F 0xffff, // U+1F950...U+1F95F 0xffff, // U+1F960...U+1F96F 0xffff, // U+1F970...U+1F97F 0xffff, // U+1F980...U+1F98F 0xffff, // U+1F990...U+1F99F 0xffff, // U+1F9A0...U+1F9AF 0xffff, // U+1F9B0...U+1F9BF 0xffff, // U+1F9C0...U+1F9CF 0xffff, // U+1F9D0...U+1F9DF 0xffff, // U+1F9E0...U+1F9EF 0xffff, // U+1F9F0...U+1F9FF 0xffff, // U+1FA00...U+1FA0F 0xffff, // U+1FA10...U+1FA1F 0xffff, // U+1FA20...U+1FA2F 0xffff, // U+1FA30...U+1FA3F 0xffff, // U+1FA40...U+1FA4F 0x000f, // U+1FA50...U+1FA5F 0x3fff, // U+1FA60...U+1FA6F 0x1fff, // U+1FA70...U+1FA7F 0x01ff, // U+1FA80...U+1FA8F 0xffff, // U+1FA90...U+1FA9F 0xffff, // U+1FAA0...U+1FAAF 0xbfff, // U+1FAB0...U+1FABF 0xc03f, // U+1FAC0...U+1FACF 0x0fff, // U+1FAD0...U+1FADF 0x01ff, // U+1FAE0...U+1FAEF 0x01ff, // U+1FAF0...U+1FAFF 0xffff, // U+1FB00...U+1FB0F 0xffff, // U+1FB10...U+1FB1F 0xffff, // U+1FB20...U+1FB2F 0xffff, // U+1FB30...U+1FB3F 0xffff, // U+1FB40...U+1FB4F 0xffff, // U+1FB50...U+1FB5F 0xffff, // U+1FB60...U+1FB6F 0xffff, // U+1FB70...U+1FB7F 0xffff, // U+1FB80...U+1FB8F 0xfff7, // U+1FB90...U+1FB9F 0xffff, // U+1FBA0...U+1FBAF 0xffff, // U+1FBB0...U+1FBBF 0x07ff, // U+1FBC0...U+1FBCF ]; pub(crate) fn is_ascii_punctuation(c: u8) -> bool { c < 128 && (PUNCT_MASKS_ASCII[(c / 16) as usize] & (1 << (c & 15))) != 0 } pub(crate) fn is_punctuation(c: char) -> bool { let cp = c as u32; if cp < 128 { return is_ascii_punctuation(cp as u8); } if cp > 0x1FBCA { return false; } let high = (cp / 16) as u16; match PUNCT_TAB.binary_search(&high) { Ok(index) => (PUNCT_MASKS[index] & (1 << (cp & 15))) != 0, _ => false, } } #[cfg(test)] mod tests { use super::{is_ascii_punctuation, is_punctuation}; #[test] fn test_ascii() { assert!(is_ascii_punctuation(b'!')); assert!(is_ascii_punctuation(b'@')); assert!(is_ascii_punctuation(b'~')); assert!(!is_ascii_punctuation(b' ')); assert!(!is_ascii_punctuation(b'0')); assert!(!is_ascii_punctuation(b'A')); assert!(!is_ascii_punctuation(0xA1)); } #[test] fn test_unicode() { assert!(is_punctuation('~')); assert!(!is_punctuation(' ')); assert!(is_punctuation('\u{00A1}')); assert!(is_punctuation('\u{060C}')); assert!(is_punctuation('\u{FF65}')); assert!(is_punctuation('\u{1BC9F}')); assert!(!is_punctuation('\u{1BCA0}')); } } pulldown-cmark-0.10.3/src/scanners.rs000064400000000000000000001261751046102023000156310ustar 00000000000000// Copyright 2015 Google Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! Scanners for fragments of CommonMark syntax use std::char; use crate::parse::HtmlScanGuard; pub(crate) use crate::puncttable::{is_ascii_punctuation, is_punctuation}; use crate::strings::CowStr; use crate::{entities, HeadingLevel}; use crate::{Alignment, LinkType}; use memchr::memchr; // sorted for binary search const HTML_TAGS: [&str; 62] = [ "address", "article", "aside", "base", "basefont", "blockquote", "body", "caption", "center", "col", "colgroup", "dd", "details", "dialog", "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hr", "html", "iframe", "legend", "li", "link", "main", "menu", "menuitem", "nav", "noframes", "ol", "optgroup", "option", "p", "param", "search", "section", "summary", "table", "tbody", "td", "tfoot", "th", "thead", "title", "tr", "track", "ul", ]; /// Analysis of the beginning of a line, including indentation and container /// markers. #[derive(Clone)] pub(crate) struct LineStart<'a> { bytes: &'a [u8], ix: usize, // The index in `bytes` after the last tab we scanned; initially // zero. // // Thus, there are no tab characters between `ix` and here, and for // the purpose of defining block structure, this position can be // considered to fall on a tab stop. // // This is only valid while scanning the initial portion of the // line; methods that work with interior structure don't bother to // update it. tab_start: usize, // In contexts where spaces help to define block structure, tabs // behave as if they were replaced by spaces with a tab stop of 4 // characters. // // If we have scanned past a tab character but not consumed all // the horizontal width it contributed, this is the number of // spaces logically remaining, before the character at `ix`. spaces_remaining: usize, // no thematic breaks can occur before this offset. // this prevents scanning over and over up to a certain point min_hrule_offset: usize, } impl<'a> LineStart<'a> { pub(crate) fn new(bytes: &[u8]) -> LineStart<'_> { LineStart { bytes, tab_start: 0, ix: 0, spaces_remaining: 0, min_hrule_offset: 0, } } /// Try to scan a number of spaces. /// /// Returns true if all spaces were consumed. /// /// Note: consumes some spaces even if not successful. pub(crate) fn scan_space(&mut self, n_space: usize) -> bool { self.scan_space_inner(n_space) == 0 } /// Scan a number of spaces up to a maximum. /// /// Returns number of spaces scanned. pub(crate) fn scan_space_upto(&mut self, n_space: usize) -> usize { n_space - self.scan_space_inner(n_space) } /// Returns unused remainder of spaces. fn scan_space_inner(&mut self, mut n_space: usize) -> usize { // Consume any common prefix between the number of spaces we // want and the number of unscanned tab-introduced spaces. let n_from_remaining = self.spaces_remaining.min(n_space); self.spaces_remaining -= n_from_remaining; n_space -= n_from_remaining; while n_space > 0 && self.ix < self.bytes.len() { match self.bytes[self.ix] { b' ' => { self.ix += 1; n_space -= 1; } b'\t' => { let spaces = 4 - (self.ix - self.tab_start) % 4; self.ix += 1; self.tab_start = self.ix; let n = spaces.min(n_space); n_space -= n; // Record the unscanned portion of the tab. self.spaces_remaining = spaces - n; } _ => break, } } n_space } /// Scan all available ASCII whitespace (not including eol). pub(crate) fn scan_all_space(&mut self) { self.spaces_remaining = 0; self.ix += self.bytes[self.ix..] .iter() .take_while(|&&b| b == b' ' || b == b'\t') .count(); } /// Determine whether we're at end of line (includes end of file). pub(crate) fn is_at_eol(&self) -> bool { self.bytes .get(self.ix) .map(|&c| c == b'\r' || c == b'\n') .unwrap_or(true) } fn scan_ch(&mut self, c: u8) -> bool { if self.ix < self.bytes.len() && self.bytes[self.ix] == c { self.ix += 1; true } else { false } } pub(crate) fn scan_blockquote_marker(&mut self) -> bool { let save = self.clone(); let _ = self.scan_space(3); if self.scan_ch(b'>') { let _ = self.scan_space(1); true } else { *self = save; false } } /// Scan a list marker. /// /// Return value is the character, the start index, and the indent in spaces. /// For ordered list markers, the character will be one of b'.' or b')'. For /// bullet list markers, it will be one of b'-', b'+', or b'*'. pub(crate) fn scan_list_marker(&mut self) -> Option<(u8, u64, usize)> { let save = self.clone(); let indent = self.scan_space_upto(4); if indent < 4 && self.ix < self.bytes.len() { let c = self.bytes[self.ix]; if c == b'-' || c == b'+' || c == b'*' { if self.ix >= self.min_hrule_offset { // there could be an hrule here if let Err(min_offset) = scan_hrule(&self.bytes[self.ix..]) { self.min_hrule_offset = min_offset; } else { *self = save; return None; } } self.ix += 1; if self.scan_space(1) || self.is_at_eol() { return self.finish_list_marker(c, 0, indent + 2); } } else if c.is_ascii_digit() { let start_ix = self.ix; let mut ix = self.ix + 1; let mut val = u64::from(c - b'0'); while ix < self.bytes.len() && ix - start_ix < 10 { let c = self.bytes[ix]; ix += 1; if c.is_ascii_digit() { val = val * 10 + u64::from(c - b'0'); } else if c == b')' || c == b'.' { self.ix = ix; if self.scan_space(1) || self.is_at_eol() { return self.finish_list_marker(c, val, indent + 1 + ix - start_ix); } else { break; } } else { break; } } } } *self = save; None } fn finish_list_marker( &mut self, c: u8, start: u64, mut indent: usize, ) -> Option<(u8, u64, usize)> { let save = self.clone(); // skip the rest of the line if it's blank if scan_blank_line(&self.bytes[self.ix..]).is_some() { return Some((c, start, indent)); } let post_indent = self.scan_space_upto(4); if post_indent < 4 { indent += post_indent; } else { *self = save; } Some((c, start, indent)) } /// Returns Some(is_checked) when a task list marker was found. Resets itself /// to original state otherwise. pub(crate) fn scan_task_list_marker(&mut self) -> Option { let save = self.clone(); self.scan_space_upto(3); if !self.scan_ch(b'[') { *self = save; return None; } let is_checked = match self.bytes.get(self.ix) { Some(&c) if is_ascii_whitespace_no_nl(c) => { self.ix += 1; false } Some(b'x') | Some(b'X') => { self.ix += 1; true } _ => { *self = save; return None; } }; if !self.scan_ch(b']') { *self = save; return None; } if !self .bytes .get(self.ix) .map(|&b| is_ascii_whitespace_no_nl(b)) .unwrap_or(false) { *self = save; return None; } Some(is_checked) } pub(crate) fn bytes_scanned(&self) -> usize { self.ix } pub(crate) fn remaining_space(&self) -> usize { self.spaces_remaining } } pub(crate) fn is_ascii_whitespace(c: u8) -> bool { (0x09..=0x0d).contains(&c) || c == b' ' } pub(crate) fn is_ascii_whitespace_no_nl(c: u8) -> bool { c == b'\t' || c == 0x0b || c == 0x0c || c == b' ' } fn is_ascii_alpha(c: u8) -> bool { c.is_ascii_alphabetic() } fn is_ascii_alphanumeric(c: u8) -> bool { matches!(c, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z') } fn is_ascii_letterdigitdash(c: u8) -> bool { c == b'-' || is_ascii_alphanumeric(c) } fn is_digit(c: u8) -> bool { c.is_ascii_digit() } fn is_valid_unquoted_attr_value_char(c: u8) -> bool { !matches!( c, b'\'' | b'"' | b' ' | b'=' | b'>' | b'<' | b'`' | b'\n' | b'\r' ) } // scan a single character pub(crate) fn scan_ch(data: &[u8], c: u8) -> usize { if !data.is_empty() && data[0] == c { 1 } else { 0 } } pub(crate) fn scan_while(data: &[u8], mut f: F) -> usize where F: FnMut(u8) -> bool, { data.iter().take_while(|&&c| f(c)).count() } pub(crate) fn scan_rev_while(data: &[u8], mut f: F) -> usize where F: FnMut(u8) -> bool, { data.iter().rev().take_while(|&&c| f(c)).count() } pub(crate) fn scan_ch_repeat(data: &[u8], c: u8) -> usize { scan_while(data, |x| x == c) } // Note: this scans ASCII whitespace only, for Unicode whitespace use // a different function. pub(crate) fn scan_whitespace_no_nl(data: &[u8]) -> usize { scan_while(data, is_ascii_whitespace_no_nl) } fn scan_attr_value_chars(data: &[u8]) -> usize { scan_while(data, is_valid_unquoted_attr_value_char) } pub(crate) fn scan_eol(bytes: &[u8]) -> Option { if bytes.is_empty() { return Some(0); } match bytes[0] { b'\n' => Some(1), b'\r' => Some(if bytes.get(1) == Some(&b'\n') { 2 } else { 1 }), _ => None, } } pub(crate) fn scan_blank_line(bytes: &[u8]) -> Option { let i = scan_whitespace_no_nl(bytes); scan_eol(&bytes[i..]).map(|n| i + n) } pub(crate) fn scan_nextline(bytes: &[u8]) -> usize { memchr(b'\n', bytes).map_or(bytes.len(), |x| x + 1) } // return: end byte for closing code fence, or None // if the line is not a closing code fence pub(crate) fn scan_closing_code_fence( bytes: &[u8], fence_char: u8, n_fence_char: usize, ) -> Option { if bytes.is_empty() { return Some(0); } let mut i = 0; let num_fence_chars_found = scan_ch_repeat(&bytes[i..], fence_char); if num_fence_chars_found < n_fence_char { return None; } i += num_fence_chars_found; let num_trailing_spaces = scan_ch_repeat(&bytes[i..], b' '); i += num_trailing_spaces; scan_eol(&bytes[i..]).map(|_| i) } // return: end byte for closing metadata block, or None // if the line is not a closing metadata block pub(crate) fn scan_closing_metadata_block(bytes: &[u8], fence_char: u8) -> Option { let mut i = 0; let mut num_fence_chars_found = scan_ch_repeat(&bytes[i..], fence_char); if num_fence_chars_found != 3 { // if YAML style metadata block the closing character can also be `.` if fence_char == b'-' { num_fence_chars_found = scan_ch_repeat(&bytes[i..], b'.'); if num_fence_chars_found != 3 { return None; } } else { return None; } } i += num_fence_chars_found; let num_trailing_spaces = scan_ch_repeat(&bytes[i..], b' '); i += num_trailing_spaces; scan_eol(&bytes[i..]).map(|_| i) } // returned pair is (number of bytes, number of spaces) pub(crate) fn calc_indent(text: &[u8], max: usize) -> (usize, usize) { let mut spaces = 0; let mut offset = 0; for (i, &b) in text.iter().enumerate() { offset = i; match b { b' ' => { spaces += 1; if spaces == max { break; } } b'\t' => { let new_spaces = spaces + 4 - (spaces & 3); if new_spaces > max { break; } spaces = new_spaces; } _ => break, } } (offset, spaces) } /// Scan hrule opening sequence. /// /// Returns Ok(x) when it finds an hrule, where x is the /// size of line containing the hrule, including the trailing newline. /// /// Returns Err(x) when it does not find an hrule and x is /// the offset in data before no hrule can appear. pub(crate) fn scan_hrule(bytes: &[u8]) -> Result { if bytes.len() < 3 { return Err(0); } let c = bytes[0]; if !(c == b'*' || c == b'-' || c == b'_') { return Err(0); } let mut n = 0; let mut i = 0; while i < bytes.len() { match bytes[i] { b'\n' | b'\r' => { i += scan_eol(&bytes[i..]).unwrap_or(0); break; } c2 if c2 == c => { n += 1; } b' ' | b'\t' => (), _ => return Err(i), } i += 1; } if n >= 3 { Ok(i) } else { Err(i) } } /// Scan an ATX heading opening sequence. /// /// Returns number of bytes in prefix and level. pub(crate) fn scan_atx_heading(data: &[u8]) -> Option { let level = scan_ch_repeat(data, b'#'); if data.get(level).copied().map_or(true, is_ascii_whitespace) { HeadingLevel::try_from(level).ok() } else { None } } /// Scan a setext heading underline. /// /// Returns number of bytes in line (including trailing newline) and level. pub(crate) fn scan_setext_heading(data: &[u8]) -> Option<(usize, HeadingLevel)> { let c = *data.first()?; let level = if c == b'=' { HeadingLevel::H1 } else if c == b'-' { HeadingLevel::H2 } else { return None; }; let mut i = 1 + scan_ch_repeat(&data[1..], c); i += scan_blank_line(&data[i..])?; Some((i, level)) } // returns number of bytes in line (including trailing // newline) and column alignments pub(crate) fn scan_table_head(data: &[u8]) -> (usize, Vec) { let (mut i, spaces) = calc_indent(data, 4); if spaces > 3 || i == data.len() { return (0, vec![]); } let mut cols = vec![]; let mut active_col = Alignment::None; let mut start_col = true; let mut found_pipe = false; let mut found_hyphen = false; let mut found_hyphen_in_col = false; if data[i] == b'|' { i += 1; found_pipe = true; } for c in &data[i..] { if let Some(n) = scan_eol(&data[i..]) { i += n; break; } match *c { b' ' => (), b':' => { active_col = match (start_col, active_col) { (true, Alignment::None) => Alignment::Left, (false, Alignment::Left) => Alignment::Center, (false, Alignment::None) => Alignment::Right, _ => active_col, }; start_col = false; } b'-' => { start_col = false; found_hyphen = true; found_hyphen_in_col = true; } b'|' => { start_col = true; found_pipe = true; cols.push(active_col); active_col = Alignment::None; if !found_hyphen_in_col { // It isn't a table head if it has back-to-back pipes. return (0, vec![]); } found_hyphen_in_col = false; } _ => { // It isn't a table head if it has characters outside the allowed set. return (0, vec![]); } } i += 1; } if !start_col { cols.push(active_col); } if !found_pipe || !found_hyphen { // It isn't a table head if it doesn't have a least one pipe or hyphen. // It's a list, a header, or a thematic break. return (0, vec![]); } (i, cols) } /// Scan code fence. /// /// Returns number of bytes scanned and the char that is repeated to make the code fence. pub(crate) fn scan_code_fence(data: &[u8]) -> Option<(usize, u8)> { let c = *data.first()?; if !(c == b'`' || c == b'~') { return None; } let i = 1 + scan_ch_repeat(&data[1..], c); if i >= 3 { if c == b'`' { let suffix = &data[i..]; let next_line = i + scan_nextline(suffix); // FIXME: make sure this is correct if suffix[..(next_line - i)].iter().any(|&b| b == b'`') { return None; } } Some((i, c)) } else { None } } /// Scan metadata block, returning the number of delimiter bytes /// (always 3 for now) and the delimiter character. /// /// Differently to code blocks, metadata blocks must be closed with the closing /// sequence not being a valid terminator the end of the file. /// /// In addition, they cannot be empty (closing sequence in the next line) and /// the next line cannot be an empty line. pub(crate) fn scan_metadata_block( data: &[u8], yaml_style_enabled: bool, pluses_style_enabled: bool, ) -> Option<(usize, u8)> { // Only if metadata blocks are enabled if yaml_style_enabled || pluses_style_enabled { let c = *data.first()?; if !((c == b'-' && yaml_style_enabled) || (c == b'+' && pluses_style_enabled)) { return None; } let i = 1 + scan_ch_repeat(&data[1..], c); // Only trailing spaces after the delimiters in the line let next_line = scan_nextline(&data[i..]); for c in &data[i..i + next_line] { if !c.is_ascii_whitespace() { return None; } } if i == 3 { // Search the closing sequence let mut j = i; let mut first_line = true; while j < data.len() { j += scan_nextline(&data[j..]); let closed = scan_closing_metadata_block(&data[j..], c).is_some(); // The first line of the metadata block cannot be an empty line // nor the end of the block if first_line { if closed || scan_blank_line(&data[j..]).is_some() { return None; } first_line = false; } if closed { return Some((i, c)); } } None } else { None } } else { None } } pub(crate) fn scan_blockquote_start(data: &[u8]) -> Option { if data.first().copied() == Some(b'>') { let space = if data.get(1).copied() == Some(b' ') { 1 } else { 0 }; Some(1 + space) } else { None } } /// return number of bytes scanned, delimiter, start index, and indent pub(crate) fn scan_listitem(bytes: &[u8]) -> Option<(usize, u8, usize, usize)> { let mut c = *bytes.first()?; let (w, start) = match c { b'-' | b'+' | b'*' => (1, 0), b'0'..=b'9' => { let (length, start) = parse_decimal(bytes, 9); c = *bytes.get(length)?; if !(c == b'.' || c == b')') { return None; } (length + 1, start) } _ => { return None; } }; // TODO: replace calc_indent with scan_leading_whitespace, for tab correctness let (mut postn, mut postindent) = calc_indent(&bytes[w..], 5); if postindent == 0 { scan_eol(&bytes[w..])?; postindent += 1; } else if postindent > 4 { postn = 1; postindent = 1; } if scan_blank_line(&bytes[w..]).is_some() { postn = 0; postindent = 1; } Some((w + postn, c, start, w + postindent)) } // returns (number of bytes, parsed decimal) fn parse_decimal(bytes: &[u8], limit: usize) -> (usize, usize) { match bytes .iter() .take(limit) .take_while(|&&b| is_digit(b)) .try_fold((0, 0usize), |(count, acc), c| { let digit = usize::from(c - b'0'); match acc .checked_mul(10) .and_then(|ten_acc| ten_acc.checked_add(digit)) { Some(number) => Ok((count + 1, number)), // stop early on overflow None => Err((count, acc)), } }) { Ok(p) | Err(p) => p, } } // returns (number of bytes, parsed hex) fn parse_hex(bytes: &[u8], limit: usize) -> (usize, usize) { match bytes .iter() .take(limit) .try_fold((0, 0usize), |(count, acc), c| { let mut c = *c; let digit = if c.is_ascii_digit() { usize::from(c - b'0') } else { // make lower case c |= 0x20; if (b'a'..=b'f').contains(&c) { usize::from(c - b'a' + 10) } else { return Err((count, acc)); } }; match acc .checked_mul(16) .and_then(|sixteen_acc| sixteen_acc.checked_add(digit)) { Some(number) => Ok((count + 1, number)), // stop early on overflow None => Err((count, acc)), } }) { Ok(p) | Err(p) => p, } } fn char_from_codepoint(input: usize) -> Option { let codepoint = input.try_into().ok()?; if codepoint == 0 { return None; } char::from_u32(codepoint) } // doesn't bother to check data[0] == '&' pub(crate) fn scan_entity(bytes: &[u8]) -> (usize, Option>) { let mut end = 1; if scan_ch(&bytes[end..], b'#') == 1 { end += 1; let (bytecount, codepoint) = if end < bytes.len() && bytes[end] | 0x20 == b'x' { end += 1; parse_hex(&bytes[end..], 6) } else { parse_decimal(&bytes[end..], 7) }; end += bytecount; return if bytecount == 0 || scan_ch(&bytes[end..], b';') == 0 { (0, None) } else { ( end + 1, Some(char_from_codepoint(codepoint).unwrap_or('\u{FFFD}').into()), ) }; } end += scan_while(&bytes[end..], is_ascii_alphanumeric); if scan_ch(&bytes[end..], b';') == 1 { if let Some(value) = entities::get_entity(&bytes[1..end]) { return (end + 1, Some(value.into())); } } (0, None) } // note: dest returned is raw, still needs to be unescaped // TODO: check that nested parens are really not allowed for refdefs // TODO(performance): this func should probably its own unescaping pub(crate) fn scan_link_dest( data: &str, start_ix: usize, max_next: usize, ) -> Option<(usize, &str)> { let bytes = &data.as_bytes()[start_ix..]; let mut i = scan_ch(bytes, b'<'); if i != 0 { // pointy links while i < bytes.len() { match bytes[i] { b'\n' | b'\r' | b'<' => return None, b'>' => return Some((i + 1, &data[(start_ix + 1)..(start_ix + i)])), b'\\' if i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) => { i += 1; } _ => {} } i += 1; } None } else { // non-pointy links let mut nest = 0; while i < bytes.len() { match bytes[i] { 0x0..=0x20 => { break; } b'(' => { if nest > max_next { return None; } nest += 1; } b')' => { if nest == 0 { break; } nest -= 1; } b'\\' if i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) => { i += 1; } _ => {} } i += 1; } if nest != 0 { return None; } Some((i, &data[start_ix..(start_ix + i)])) } } /// Returns bytes scanned fn scan_attribute_name(data: &[u8]) -> Option { let (&c, tail) = data.split_first()?; if is_ascii_alpha(c) || c == b'_' || c == b':' { Some( 1 + scan_while(tail, |c| { is_ascii_alphanumeric(c) || c == b'_' || c == b'.' || c == b':' || c == b'-' }), ) } else { None } } /// Returns the index immediately following the attribute on success. /// The argument `buffer_ix` refers to the index into `data` from which we /// should copy into `buffer` when we find bytes to skip. fn scan_attribute( data: &[u8], mut ix: usize, newline_handler: Option<&dyn Fn(&[u8]) -> usize>, buffer: &mut Vec, buffer_ix: &mut usize, ) -> Option { ix += scan_attribute_name(&data[ix..])?; let ix_after_attribute = ix; ix = scan_whitespace_with_newline_handler_without_buffer(data, ix, newline_handler)?; if scan_ch(&data[ix..], b'=') == 1 { ix = scan_whitespace_with_newline_handler(data, ix_after_attribute, newline_handler, buffer, buffer_ix)?; ix += 1; ix = scan_whitespace_with_newline_handler(data, ix, newline_handler, buffer, buffer_ix)?; ix = scan_attribute_value(data, ix, newline_handler, buffer, buffer_ix)?; Some(ix) } else { // Leave whitespace for next attribute. Some(ix_after_attribute) } } /// Scans whitespace and possibly newlines according to the /// behavior defined by the newline handler. When bytes are skipped, /// all preceding non-skipped bytes are pushed to the buffer. fn scan_whitespace_with_newline_handler( data: &[u8], mut i: usize, newline_handler: Option<&dyn Fn(&[u8]) -> usize>, buffer: &mut Vec, buffer_ix: &mut usize, ) -> Option { while i < data.len() { if !is_ascii_whitespace(data[i]) { return Some(i); } if let Some(eol_bytes) = scan_eol(&data[i..]) { let handler = newline_handler?; i += eol_bytes; let skipped_bytes = handler(&data[i..]); if skipped_bytes > 0 { buffer.extend(&data[*buffer_ix..i]); *buffer_ix = i + skipped_bytes; } i += skipped_bytes; } else { i += 1; } } Some(i) } /// Scans whitespace and possible newlines according to the behavior defined /// by the newline handler. /// /// Unlike [`scan_whitespace_with_newline_handler`], this function doesn't /// copy skipped data into a buffer. Typically, if this function /// returns `Some`, a call to `scan_whitespace_with_newline_handler` will /// soon follow. fn scan_whitespace_with_newline_handler_without_buffer( data: &[u8], mut i: usize, newline_handler: Option<&dyn Fn(&[u8]) -> usize>, ) -> Option { while i < data.len() { if !is_ascii_whitespace(data[i]) { return Some(i); } if let Some(eol_bytes) = scan_eol(&data[i..]) { let handler = newline_handler?; i += eol_bytes; let skipped_bytes = handler(&data[i..]); i += skipped_bytes; } else { i += 1; } } Some(i) } /// Returns the index immediately following the attribute value on success. fn scan_attribute_value( data: &[u8], mut i: usize, newline_handler: Option<&dyn Fn(&[u8]) -> usize>, buffer: &mut Vec, buffer_ix: &mut usize, ) -> Option { match *data.get(i)? { b @ b'"' | b @ b'\'' => { i += 1; while i < data.len() { if data[i] == b { return Some(i + 1); } if let Some(eol_bytes) = scan_eol(&data[i..]) { let handler = newline_handler?; i += eol_bytes; let skipped_bytes = handler(&data[i..]); if skipped_bytes > 0 { buffer.extend(&data[*buffer_ix..i]); *buffer_ix = i + skipped_bytes; } i += skipped_bytes; } else { i += 1; } } return None; } b' ' | b'=' | b'>' | b'<' | b'`' | b'\n' | b'\r' => { return None; } _ => { // unquoted attribute value i += scan_attr_value_chars(&data[i..]); } } Some(i) } // Remove backslash escapes and resolve entities pub(crate) fn unescape<'a, I: Into>>(input: I, is_in_table: bool) -> CowStr<'a> { let input = input.into(); let mut result = String::new(); let mut mark = 0; let mut i = 0; let bytes = input.as_bytes(); while i < bytes.len() { match bytes[i] { // Tables are special, because they're parsed as-if the tables // were parsed in a discrete pass, changing `\|` to `|`, and then // passing the changed string to the inline parser. b'\\' if is_in_table && i + 2 < bytes.len() && bytes[i + 1] == b'\\' && bytes[i + 2] == b'|' => { // even number of `\`s before pipe // odd number is handled in the normal way below result.push_str(&input[mark..i]); mark = i + 2; i += 3; } b'\\' if i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) => { result.push_str(&input[mark..i]); mark = i + 1; i += 2; } b'&' => match scan_entity(&bytes[i..]) { (n, Some(value)) => { result.push_str(&input[mark..i]); result.push_str(&value); i += n; mark = i; } _ => i += 1, }, b'\r' => { result.push_str(&input[mark..i]); i += 1; mark = i; } _ => i += 1, } } if mark == 0 { input } else { result.push_str(&input[mark..]); result.into() } } /// Assumes `data` is preceded by `<`. pub(crate) fn starts_html_block_type_6(data: &[u8]) -> bool { let i = scan_ch(data, b'/'); let tail = &data[i..]; let n = scan_while(tail, is_ascii_alphanumeric); if !is_html_tag(&tail[..n]) { return false; } // Starting condition says the next byte must be either a space, a tab, // the end of the line, the string >, or the string /> let tail = &tail[n..]; tail.is_empty() || tail[0] == b' ' || tail[0] == b'\t' || tail[0] == b'\r' || tail[0] == b'\n' || tail[0] == b'>' || tail.len() >= 2 && &tail[..2] == b"/>" } fn is_html_tag(tag: &[u8]) -> bool { HTML_TAGS .binary_search_by(|probe| { let probe_bytes_iter = probe.as_bytes().iter(); let tag_bytes_iter = tag.iter(); probe_bytes_iter .zip(tag_bytes_iter) .find_map(|(&a, &b)| { // We can compare case insensitively because the probes are // all lower case alpha strings. match a.cmp(&(b | 0x20)) { std::cmp::Ordering::Equal => None, inequality => Some(inequality), } }) .unwrap_or_else(|| probe.len().cmp(&tag.len())) }) .is_ok() } /// Assumes that `data` starts with `<`. /// Returns the index into data directly after the html tag on success. pub(crate) fn scan_html_type_7(data: &[u8]) -> Option { // Block type html does not allow for newlines, so we // do not pass a newline handler. let (_span, i) = scan_html_block_inner(data, None)?; scan_blank_line(&data[i..])?; Some(i) } /// Assumes that `data` starts with `<`. /// Returns the number of bytes scanned and the html in case of /// success. /// When some bytes were skipped, because the html was split over /// multiple leafs (e.g. over multiple lines in a blockquote), /// the html is returned as a vector of bytes. /// If no bytes were skipped, the buffer will be empty. pub(crate) fn scan_html_block_inner( data: &[u8], newline_handler: Option<&dyn Fn(&[u8]) -> usize>, ) -> Option<(Vec, usize)> { let mut buffer = Vec::new(); let mut last_buf_index = 0; let close_tag_bytes = scan_ch(&data[1..], b'/'); let l = scan_while(&data[(1 + close_tag_bytes)..], is_ascii_alpha); if l == 0 { return None; } let mut i = 1 + close_tag_bytes + l; i += scan_while(&data[i..], is_ascii_letterdigitdash); if close_tag_bytes == 0 { loop { let old_i = i; loop { i += scan_whitespace_no_nl(&data[i..]); if let Some(eol_bytes) = scan_eol(&data[i..]) { if eol_bytes == 0 { return None; } let handler = newline_handler?; i += eol_bytes; let skipped_bytes = handler(&data[i..]); let data_len = data.len() - i; debug_assert!( skipped_bytes <= data_len, "Handler tried to skip too many bytes, fed {}, skipped {}", data_len, skipped_bytes ); if skipped_bytes > 0 { buffer.extend(&data[last_buf_index..i]); i += skipped_bytes; last_buf_index = i; } } else { break; } } if let Some(b'/') | Some(b'>') = data.get(i) { break; } if old_i == i { // No whitespace, which is mandatory. return None; } i = scan_attribute(data, i, newline_handler, &mut buffer, &mut last_buf_index)?; } } i += scan_whitespace_no_nl(&data[i..]); if close_tag_bytes == 0 { i += scan_ch(&data[i..], b'/'); } if scan_ch(&data[i..], b'>') == 0 { None } else { i += 1; if !buffer.is_empty() { buffer.extend(&data[last_buf_index..i]); } Some((buffer, i)) } } /// Returns (next_byte_offset, uri, type) pub(crate) fn scan_autolink(text: &str, start_ix: usize) -> Option<(usize, CowStr<'_>, LinkType)> { scan_uri(text, start_ix) .map(|(bytes, uri)| (bytes, uri, LinkType::Autolink)) .or_else(|| scan_email(text, start_ix).map(|(bytes, uri)| (bytes, uri, LinkType::Email))) } /// Returns (next_byte_offset, uri) fn scan_uri(text: &str, start_ix: usize) -> Option<(usize, CowStr<'_>)> { let bytes = &text.as_bytes()[start_ix..]; // scheme's first byte must be an ascii letter if bytes.is_empty() || !is_ascii_alpha(bytes[0]) { return None; } let mut i = 1; while i < bytes.len() { let c = bytes[i]; i += 1; match c { c if is_ascii_alphanumeric(c) => (), b'.' | b'-' | b'+' => (), b':' => break, _ => return None, } } // scheme length must be between 2 and 32 characters long. scheme // must be followed by colon if !(3..=33).contains(&i) { return None; } while i < bytes.len() { match bytes[i] { b'>' => return Some((start_ix + i + 1, text[start_ix..(start_ix + i)].into())), b'\0'..=b' ' | b'<' => return None, _ => (), } i += 1; } None } /// Returns (next_byte_offset, email) fn scan_email(text: &str, start_ix: usize) -> Option<(usize, CowStr<'_>)> { // using a regex library would be convenient, but doing it by hand is not too bad let bytes = &text.as_bytes()[start_ix..]; let mut i = 0; while i < bytes.len() { let c = bytes[i]; i += 1; match c { c if is_ascii_alphanumeric(c) => (), b'.' | b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'/' | b'=' | b'?' | b'^' | b'_' | b'`' | b'{' | b'|' | b'}' | b'~' | b'-' => (), b'@' if i > 1 => break, _ => return None, } } loop { let label_start_ix = i; let mut fresh_label = true; while i < bytes.len() { match bytes[i] { c if is_ascii_alphanumeric(c) => (), b'-' if fresh_label => { return None; } b'-' => (), _ => break, } fresh_label = false; i += 1; } if i == label_start_ix || i - label_start_ix > 63 || bytes[i - 1] == b'-' { return None; } if scan_ch(&bytes[i..], b'.') == 0 { break; } i += 1; } if scan_ch(&bytes[i..], b'>') == 0 { return None; } Some((start_ix + i + 1, text[start_ix..(start_ix + i)].into())) } /// Scan comment, declaration, or CDATA section, with initial " Option { let c = *bytes.get(ix)?; ix += 1; match c { // An HTML comment consists of ``, ``, or ``, and `-->`. b'-' => { // HTML comment needs two hyphens after the !. if *bytes.get(ix)? != b'-' { return None; } // Yes, we're intentionally going backwards. // We want the cursor to point here: // // ` case is covered by the loop below. ix -= 1; while let Some(x) = memchr(b'-', &bytes[ix..]) { ix += x + 1; if scan_ch(&bytes[ix..], b'-') == 1 && scan_ch(&bytes[ix + 1..], b'>') == 1 { return Some(ix + 2); } } None } // A CDATA section consists of the string ``, and the string `]]>`. b'[' if bytes[ix..].starts_with(b"CDATA[") && ix > scan_guard.cdata => { ix += b"CDATA[".len(); ix = memchr(b']', &bytes[ix..]).map_or(bytes.len(), |x| ix + x); let close_brackets = scan_ch_repeat(&bytes[ix..], b']'); ix += close_brackets; if close_brackets == 0 || scan_ch(&bytes[ix..], b'>') == 0 { scan_guard.cdata = ix; None } else { Some(ix + 1) } } // A declaration consists of the string `, and the character >. _ if c.is_ascii_alphabetic() && ix > scan_guard.declaration => { ix = memchr(b'>', &bytes[ix..]).map_or(bytes.len(), |x| ix + x); if scan_ch(&bytes[ix..], b'>') == 0 { scan_guard.declaration = ix; None } else { Some(ix + 1) } } _ => None, } } /// Scan processing directive, with initial " Option { if ix <= scan_guard.processing { return None; } while let Some(offset) = memchr(b'?', &bytes[ix..]) { ix += offset + 1; if scan_ch(&bytes[ix..], b'>') == 1 { return Some(ix + 1); } } scan_guard.processing = ix; None } #[cfg(test)] mod test { use super::*; #[test] fn overflow_list() { assert!( scan_listitem(b"4444444444444444444444444444444444444444444444444444444444!").is_none() ); } #[test] fn overflow_by_addition() { assert!(scan_listitem(b"1844674407370955161615!").is_none()); } #[test] fn good_emails() { const EMAILS: &[&str] = &[ "", "", "", "", ]; for email in EMAILS { assert!(scan_email(email, 1).is_some()); } } #[test] fn bad_emails() { const EMAILS: &[&str] = &[ "<@b.c>", "", "", "", "", "<\"noquotes\"@example.com>", "", ]; for email in EMAILS { assert!(scan_email(email, 1).is_none()); } } } pulldown-cmark-0.10.3/src/strings.rs000064400000000000000000000226441046102023000155020ustar 00000000000000use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::str::from_utf8; const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::() - 2; /// Returned when trying to convert a `&str` into a `InlineStr` /// but it fails because it doesn't fit. #[derive(Debug)] pub struct StringTooLongError; /// An inline string that can contain almost three words /// of utf-8 text. #[derive(Debug, Clone, Copy, Eq)] pub struct InlineStr { inner: [u8; MAX_INLINE_STR_LEN], len: u8, } impl AsRef for InlineStr { fn as_ref(&self) -> &str { self.deref() } } impl Hash for InlineStr { fn hash(&self, state: &mut H) { self.deref().hash(state); } } impl From for InlineStr { fn from(c: char) -> Self { let mut inner = [0u8; MAX_INLINE_STR_LEN]; c.encode_utf8(&mut inner); let len = c.len_utf8() as u8; Self { inner, len } } } impl std::cmp::PartialEq for InlineStr { fn eq(&self, other: &InlineStr) -> bool { self.deref() == other.deref() } } impl TryFrom<&str> for InlineStr { type Error = StringTooLongError; fn try_from(s: &str) -> Result { let len = s.len(); if len <= MAX_INLINE_STR_LEN { let mut inner = [0u8; MAX_INLINE_STR_LEN]; inner[..len].copy_from_slice(s.as_bytes()); let len = len as u8; Ok(Self { inner, len }) } else { Err(StringTooLongError) } } } impl Deref for InlineStr { type Target = str; fn deref(&self) -> &str { let len = self.len as usize; from_utf8(&self.inner[..len]).unwrap() } } impl fmt::Display for InlineStr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_ref()) } } /// A copy-on-write string that can be owned, borrowed /// or inlined. /// /// It is three words long. #[derive(Debug, Eq)] pub enum CowStr<'a> { /// An owned, immutable string. Boxed(Box), /// A borrowed string. Borrowed(&'a str), /// A short inline string. Inlined(InlineStr), } #[cfg(feature = "serde")] mod serde_impl { use super::CowStr; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; impl<'a> Serialize for CowStr<'a> { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(self.as_ref()) } } struct CowStrVisitor; impl<'de> de::Visitor<'de> for CowStrVisitor { type Value = CowStr<'de>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string") } fn visit_borrowed_str(self, v: &'de str) -> Result where E: de::Error, { Ok(CowStr::Borrowed(v)) } } impl<'a, 'de: 'a> Deserialize<'de> for CowStr<'a> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(CowStrVisitor) } } } impl<'a> AsRef for CowStr<'a> { fn as_ref(&self) -> &str { self.deref() } } impl<'a> Hash for CowStr<'a> { fn hash(&self, state: &mut H) { self.deref().hash(state); } } impl<'a> std::clone::Clone for CowStr<'a> { fn clone(&self) -> Self { match self { CowStr::Boxed(s) => match InlineStr::try_from(&**s) { Ok(inline) => CowStr::Inlined(inline), Err(..) => CowStr::Boxed(s.clone()), }, CowStr::Borrowed(s) => CowStr::Borrowed(s), CowStr::Inlined(s) => CowStr::Inlined(*s), } } } impl<'a> std::cmp::PartialEq> for CowStr<'a> { fn eq(&self, other: &CowStr<'_>) -> bool { self.deref() == other.deref() } } impl<'a> From<&'a str> for CowStr<'a> { fn from(s: &'a str) -> Self { CowStr::Borrowed(s) } } impl<'a> From for CowStr<'a> { fn from(s: String) -> Self { CowStr::Boxed(s.into_boxed_str()) } } impl<'a> From for CowStr<'a> { fn from(c: char) -> Self { CowStr::Inlined(c.into()) } } impl<'a> From> for CowStr<'a> { fn from(s: Cow<'a, str>) -> Self { match s { Cow::Borrowed(s) => CowStr::Borrowed(s), Cow::Owned(s) => CowStr::Boxed(s.into_boxed_str()), } } } impl<'a> From> for Cow<'a, str> { fn from(s: CowStr<'a>) -> Self { match s { CowStr::Boxed(s) => Cow::Owned(s.to_string()), CowStr::Inlined(s) => Cow::Owned(s.to_string()), CowStr::Borrowed(s) => Cow::Borrowed(s), } } } impl<'a> From> for CowStr<'a> { fn from(s: Cow<'a, char>) -> Self { CowStr::Inlined(InlineStr::from(*s)) } } impl<'a> Deref for CowStr<'a> { type Target = str; fn deref(&self) -> &str { match self { CowStr::Boxed(ref b) => b, CowStr::Borrowed(b) => b, CowStr::Inlined(ref s) => s.deref(), } } } impl<'a> Borrow for CowStr<'a> { fn borrow(&self) -> &str { self.deref() } } impl<'a> CowStr<'a> { pub fn into_string(self) -> String { match self { CowStr::Boxed(b) => b.into(), CowStr::Borrowed(b) => b.to_owned(), CowStr::Inlined(s) => s.deref().to_owned(), } } } impl<'a> fmt::Display for CowStr<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_ref()) } } #[cfg(test)] mod test_special_string { use super::*; #[test] fn inlinestr_ascii() { let s: InlineStr = 'a'.into(); assert_eq!("a", s.deref()); } #[test] fn inlinestr_unicode() { let s: InlineStr = '🍔'.into(); assert_eq!("🍔", s.deref()); } #[test] fn cowstr_size() { let size = std::mem::size_of::(); let word_size = std::mem::size_of::(); assert_eq!(3 * word_size, size); } #[test] fn cowstr_char_to_string() { let c = '藏'; let smort: CowStr = c.into(); let owned: String = smort.to_string(); let expected = "藏".to_owned(); assert_eq!(expected, owned); } #[test] fn max_inline_str_len_atleast_four() { // we need 4 bytes to store a char assert!(MAX_INLINE_STR_LEN >= 4); } #[test] #[cfg(target_pointer_width = "64")] fn inlinestr_fits_twentytwo() { let s = "0123456789abcdefghijkl"; let stack_str = InlineStr::try_from(s).unwrap(); assert_eq!(stack_str.deref(), s); } #[test] #[cfg(target_pointer_width = "64")] fn inlinestr_not_fits_twentythree() { let s = "0123456789abcdefghijklm"; let _stack_str = InlineStr::try_from(s).unwrap_err(); } #[test] #[cfg(target_pointer_width = "64")] fn small_boxed_str_clones_to_stack() { let s = "0123456789abcde".to_owned(); let smort: CowStr = s.into(); let smort_clone = smort.clone(); if let CowStr::Inlined(..) = smort_clone { } else { panic!("Expected a Inlined variant!"); } } #[test] fn cow_to_cow_str() { let s = "some text"; let cow = Cow::Borrowed(s); let actual = CowStr::from(cow); let expected = CowStr::Borrowed(s); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); let s = "some text".to_string(); let cow: Cow = Cow::Owned(s.clone()); let actual = CowStr::from(cow); let expected = CowStr::Boxed(s.into_boxed_str()); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); } #[test] fn cow_str_to_cow() { let s = "some text"; let cow_str = CowStr::Borrowed(s); let actual = Cow::from(cow_str); let expected = Cow::Borrowed(s); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); let s = "s"; let inline_str: InlineStr = InlineStr::try_from(s).unwrap(); let cow_str = CowStr::Inlined(inline_str); let actual = Cow::from(cow_str); let expected: Cow = Cow::Owned(s.to_string()); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); let s = "s"; let cow_str = CowStr::Boxed(s.to_string().into_boxed_str()); let actual = Cow::from(cow_str); let expected: Cow = Cow::Owned(s.to_string()); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); } #[test] fn cow_char_to_cow_str() { let c = 'c'; let cow: Cow = Cow::Owned(c); let actual = CowStr::from(cow); let expected = CowStr::Inlined(InlineStr::from(c)); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); let c = 'c'; let cow: Cow = Cow::Borrowed(&c); let actual = CowStr::from(cow); let expected = CowStr::Inlined(InlineStr::from(c)); assert_eq!(actual, expected); assert!(variant_eq(&actual, &expected)); } fn variant_eq(a: &T, b: &T) -> bool { std::mem::discriminant(a) == std::mem::discriminant(b) } } pulldown-cmark-0.10.3/src/tree.rs000064400000000000000000000211421046102023000147400ustar 00000000000000// Copyright 2018 Google LLC // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. //! A Vec-based container for a tree structure. use std::num::NonZeroUsize; use std::ops::{Add, Sub}; use crate::parse::{Item, ItemBody}; #[derive(Debug, Eq, PartialEq, Copy, Clone, PartialOrd)] pub(crate) struct TreeIndex(NonZeroUsize); impl TreeIndex { fn new(i: usize) -> Self { TreeIndex(NonZeroUsize::new(i).unwrap()) } pub fn get(self) -> usize { self.0.get() } } impl Add for TreeIndex { type Output = TreeIndex; fn add(self, rhs: usize) -> Self { let inner = self.0.get() + rhs; TreeIndex::new(inner) } } impl Sub for TreeIndex { type Output = TreeIndex; fn sub(self, rhs: usize) -> Self { let inner = self.0.get().checked_sub(rhs).unwrap(); TreeIndex::new(inner) } } #[derive(Debug, Clone, Copy)] pub(crate) struct Node { pub child: Option, pub next: Option, pub item: T, } /// A tree abstraction, intended for fast building as a preorder traversal. #[derive(Clone)] pub(crate) struct Tree { nodes: Vec>, spine: Vec, // indices of nodes on path to current node cur: Option, } impl Tree { // Indices start at one, so we place a dummy value at index zero. // The alternative would be subtracting one from every TreeIndex // every time we convert it to usize to index our nodes. pub(crate) fn with_capacity(cap: usize) -> Tree { let mut nodes = Vec::with_capacity(cap); nodes.push(Node { child: None, next: None, item: ::default(), }); Tree { nodes, spine: Vec::new(), cur: None, } } /// Returns the index of the element currently in focus. pub(crate) fn cur(&self) -> Option { self.cur } /// Append one item to the current position in the tree. pub(crate) fn append(&mut self, item: T) -> TreeIndex { let ix = self.create_node(item); let this = Some(ix); if let Some(ix) = self.cur { self[ix].next = this; } else if let Some(&parent) = self.spine.last() { self[parent].child = this; } self.cur = this; ix } /// Create an isolated node. pub(crate) fn create_node(&mut self, item: T) -> TreeIndex { let this = self.nodes.len(); self.nodes.push(Node { child: None, next: None, item, }); TreeIndex::new(this) } /// Push down one level, so that new items become children of the current node. /// The new focus index is returned. pub(crate) fn push(&mut self) -> TreeIndex { let cur_ix = self.cur.unwrap(); self.spine.push(cur_ix); self.cur = self[cur_ix].child; cur_ix } /// Pop back up a level. pub(crate) fn pop(&mut self) -> Option { let ix = Some(self.spine.pop()?); self.cur = ix; ix } /// Remove the last node, as `pop` but removing it. pub(crate) fn remove_node(&mut self) -> Option { let ix = self.spine.pop()?; self.cur = Some(ix); self.nodes.pop()?; self[ix].child = None; Some(ix) } /// Look at the parent node. pub(crate) fn peek_up(&self) -> Option { self.spine.last().copied() } /// Look at grandparent node. pub(crate) fn peek_grandparent(&self) -> Option { if self.spine.len() >= 2 { Some(self.spine[self.spine.len() - 2]) } else { None } } /// Returns true when there are no nodes other than the root node /// in the tree, false otherwise. pub(crate) fn is_empty(&self) -> bool { self.nodes.len() <= 1 } /// Returns the length of the spine. pub(crate) fn spine_len(&self) -> usize { self.spine.len() } /// Resets the focus to the first node added to the tree, if it exists. pub(crate) fn reset(&mut self) { self.cur = if self.is_empty() { None } else { Some(TreeIndex::new(1)) }; self.spine.clear(); } /// Walks the spine from a root node up to, but not including, the current node. pub(crate) fn walk_spine(&self) -> impl std::iter::DoubleEndedIterator { self.spine.iter() } /// Moves focus to the next sibling of the given node. pub(crate) fn next_sibling(&mut self, cur_ix: TreeIndex) -> Option { self.cur = self[cur_ix].next; self.cur } } impl Tree { /// Truncates the preceding siblings to the given end position, /// and returns the new current node. pub(crate) fn truncate_siblings(&mut self, end_byte_ix: usize) { let parent_ix = self.peek_up().unwrap(); let mut next_child_ix = self[parent_ix].child; let mut prev_child_ix = None; // drop or truncate children based on its range while let Some(child_ix) = next_child_ix { let child_end = self[child_ix].item.end; if child_end < end_byte_ix { // preserve this node, and go to the next prev_child_ix = Some(child_ix); next_child_ix = self[child_ix].next; continue; } else if child_end == end_byte_ix { // this will be the last node self[child_ix].next = None; // focus to the new last child (this node) self.cur = Some(child_ix); } else if self[child_ix].item.start == end_byte_ix { // check whether the previous character is a backslash let is_previous_char_backslash_escape = match self[child_ix].item.body { ItemBody::Text { backslash_escaped } => backslash_escaped, _ => false, }; if is_previous_char_backslash_escape { // rescue the backslash as a plain text content let last_byte_ix = end_byte_ix - 1; self[child_ix].item.start = last_byte_ix; self[child_ix].item.end = end_byte_ix; self.cur = Some(child_ix); } else if let Some(prev_child_ix) = prev_child_ix { // the node will become empty. drop the node // a preceding sibling exists self[prev_child_ix].next = None; self.cur = Some(prev_child_ix); } else { // no preceding siblings. remove the node from the parent self[parent_ix].child = None; self.cur = None; } } else { debug_assert!(self[child_ix].item.start < end_byte_ix); debug_assert!(end_byte_ix < child_end); // truncate the node self[child_ix].item.end = end_byte_ix; self[child_ix].next = None; // focus to the new last child self.cur = Some(child_ix); } break; } } } impl std::fmt::Debug for Tree where T: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn debug_tree( tree: &Tree, cur: TreeIndex, indent: usize, f: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result where T: std::fmt::Debug, { for _ in 0..indent { write!(f, " ")?; } writeln!(f, "{:?}", &tree[cur].item)?; if let Some(child_ix) = tree[cur].child { debug_tree(tree, child_ix, indent + 1, f)?; } if let Some(next_ix) = tree[cur].next { debug_tree(tree, next_ix, indent, f)?; } Ok(()) } if self.nodes.len() > 1 { let cur = TreeIndex(NonZeroUsize::new(1).unwrap()); debug_tree(self, cur, 0, f) } else { write!(f, "Empty tree") } } } impl std::ops::Index for Tree { type Output = Node; fn index(&self, ix: TreeIndex) -> &Self::Output { self.nodes.index(ix.get()) } } impl std::ops::IndexMut for Tree { fn index_mut(&mut self, ix: TreeIndex) -> &mut Node { self.nodes.index_mut(ix.get()) } } pulldown-cmark-0.10.3/src/utils.rs000064400000000000000000000114731046102023000151470ustar 00000000000000//! Miscellaneous utilities to incraese comfort. //! Special thanks to: //! //! - . //! Its author authorized the use of this GPL code in this project in //! . //! //! - . //! Its author proposed the solution in //! . use crate::{ BrokenLinkCallback, CowStr, DefaultBrokenLinkCallback, Event, OffsetIter, Options, Parser, }; use std::{iter::Peekable, ops::Range}; /// Merge consecutive `Event::Text` events into only one. #[derive(Debug)] pub struct TextMergeStream<'a, I> { iter: I, last_event: Option>, } impl<'a, I> TextMergeStream<'a, I> where I: Iterator>, { pub fn new(iter: I) -> Self { Self { iter, last_event: None, } } } impl<'a, I> Iterator for TextMergeStream<'a, I> where I: Iterator>, { type Item = Event<'a>; fn next(&mut self) -> Option { match (self.last_event.take(), self.iter.next()) { (Some(Event::Text(last_text)), Some(Event::Text(next_text))) => { // We need to start merging consecutive text events together into one let mut string_buf: String = last_text.into_string(); string_buf.push_str(&next_text); loop { // Avoid recursion to avoid stack overflow and to optimize concatenation match self.iter.next() { Some(Event::Text(next_text)) => { string_buf.push_str(&next_text); } next_event => { self.last_event = next_event; if string_buf.is_empty() { // Discard text event(s) altogether if there is no text break self.next(); } else { break Some(Event::Text(CowStr::Boxed( string_buf.into_boxed_str(), ))); } } } } } (None, Some(next_event)) => { // This only happens once during the first iteration and if there are items self.last_event = Some(next_event); self.next() } (None, None) => { // This happens when the iterator is depleted None } (last_event, next_event) => { // The ordinary case, emit one event after the other without modification self.last_event = next_event; last_event } } } } /// Merge consecutive `Event::Text` events into only one with offsets. #[derive(Debug)] pub struct TextMergeWithOffset<'input, F = DefaultBrokenLinkCallback> where F: BrokenLinkCallback<'input>, { source: &'input str, parser: Peekable>, } impl<'input, F> TextMergeWithOffset<'input, F> where F: BrokenLinkCallback<'input>, { pub fn new_ext(source: &'input str, options: Options) -> Self { Self { source, parser: Parser::new_with_broken_link_callback(source, options, None) .into_offset_iter() .peekable(), } } pub fn new_ext_with_broken_link_callback( source: &'input str, options: Options, callback: Option, ) -> Self { Self { source, parser: Parser::new_with_broken_link_callback(source, options, callback) .into_offset_iter() .peekable(), } } } impl<'input, F> Iterator for TextMergeWithOffset<'input, F> where F: BrokenLinkCallback<'input>, { type Item = (Event<'input>, Range); fn next(&mut self) -> Option { let is_empty_text = |x: Option<&(Event<'input>, Range)>| match x { Some(e) => matches!(&e.0, Event::Text(t) if t.is_empty()), None => false, }; while is_empty_text(self.parser.peek()) { self.parser.next(); } match self.parser.peek()? { (Event::Text(_), range) => { let start = range.start; let mut end = range.end; while let Some((Event::Text(_), _)) = self.parser.peek() { end = self.parser.next().unwrap().1.end; } Some((Event::Text(self.source[start..end].into()), start..end)) } _ => self.parser.next(), } } } pulldown-cmark-0.10.3/tests/errors.rs000064400000000000000000000043221046102023000156710ustar 00000000000000use pulldown_cmark::{Options, Parser}; fn parse(md: &str) { let parser = Parser::new(md); for _ in parser {} } fn parse_all_options(md: &str) { let parser = Parser::new_ext(md, Options::all()); for _ in parser {} } #[test] fn test_lists_inside_code_spans() { parse( r"- ` x ** * `", ); } #[test] fn test_fuzzer_input_1() { parse(">\n >>>\n\t[]"); } #[test] fn test_fuzzer_input_7() { parse_all_options("[][{]}\n-"); } #[test] fn test_fuzzer_input_8() { parse_all_options("a\n \u{c}{}\n-"); } #[test] fn test_fuzzer_input_9() { parse_all_options("a\n \u{c}{}\\\n-"); } #[test] fn test_fuzzer_input_10() { parse_all_options("[[ \t\n \u{c}\u{c}\u{c}\u{c}\u{c} {}\n-\r\u{e}\u{0}\u{0}{# }\n\u{b}\u{b}\u{b}\u{b}\u{b}\u{b}\u{b}\u{b}\u{b}\u{b}\u{0}\u{0}"); } #[test] fn test_fuzzer_input_11() { parse_all_options( "[[\u{c}\u{c} \t\n \u{c}\u{c}\u{c}\u{c}\u{c}\u{c}\u{c}\u{c}\u{c} {}\n-\r\u{e}", ); } #[test] fn test_fuzzer_input_12() { parse_all_options("\u{c}-\n\u{c}\n-"); } #[test] fn test_fuzzer_input_13() { // Does not fail with Options::all(): Parser::new_ext( ".\r> ^](\r\u{c}\r\0\0\r.\r[^\0\0\\\0\0\0^^^^^]", Options::ENABLE_FOOTNOTES, ); } #[test] fn test_wrong_code_block() { parse( r##"``` * ``` "##, ); } #[test] fn test_unterminated_link() { parse("[](\\"); } #[test] fn test_unterminated_autolink() { parse("") } pulldown-cmark-0.10.3/tests/html.rs000064400000000000000000000162451046102023000153300ustar 00000000000000// Tests for HTML spec. #![cfg(feature = "html")] use pulldown_cmark::{html, BrokenLink, Options, Parser}; #[test] fn html_test_1() { let original = r##"Little header "##; let expected = r##"

Little header

"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_2() { let original = r##"Little header "##; let expected = r##"

Little header

"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_3() { let original = r##"Little header

Useless

?>"##; let expected = r##"

Little header

Useless

?>"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_4() { let original = r##"Little header "##; let expected = r##"

Little header

"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_5() { let original = r##"Little header

Useless

]]>"##; let expected = r##"

Little header

Useless

]]>"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_6() { let original = r##"Little header "##; let expected = r##"

Little header

"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_7() { let original = r##"Little header ----------- "##; let expected = r##"

Little header

"##; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_8() { let original = "A | B\n---|---\nfoo | bar"; let expected = r##"
AB
foobar
"##; let mut s = String::new(); let mut opts = Options::empty(); opts.insert(Options::ENABLE_TABLES); html::push_html(&mut s, Parser::new_ext(original, opts)); assert_eq!(expected, s); } #[test] fn html_test_9() { let original = "---"; let expected = "
\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_10() { let original = "* * *"; let expected = "
\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_11() { let original = "hi ~~no~~"; let expected = "

hi ~~no~~

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn html_test_broken_callback() { let original = r##"[foo], [bar], [baz], [baz]: https://example.org "##; let expected = r##"

foo, [bar], baz,

"##; use pulldown_cmark::{html, Options, Parser}; let mut s = String::new(); let mut callback = |broken_link: BrokenLink| { if &*broken_link.reference == "foo" || &*broken_link.reference == "baz" { Some(("https://replaced.example.org".into(), "some title".into())) } else { None } }; let p = Parser::new_with_broken_link_callback(original, Options::empty(), Some(&mut callback)); html::push_html(&mut s, p); assert_eq!(expected, s); } #[test] fn newline_in_code() { let originals = ["`\n `x", "` \n`x"]; let expected = "

x

\n"; for original in originals { let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } } #[test] fn newline_start_end_of_code() { let original = "`\nx\n`x"; let expected = "

xx

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } // https://github.com/raphlinus/pulldown-cmark/issues/715 #[test] fn trim_space_and_tab_at_end_of_paragraph() { let original = "one\ntwo \t"; let expected = "

one\ntwo

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn newline_within_code() { let originals = ["`\nx \ny\n`x", "`x \ny`x", "`x\n y`x"]; let expected = "

x yx

\n"; for original in originals { let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } } #[test] fn trim_space_tab_nl_at_end_of_paragraph() { let original = "one\ntwo \t\n"; let expected = "

one\ntwo

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn trim_space_nl_at_end_of_paragraph() { let original = "one\ntwo \n"; let expected = "

one\ntwo

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } #[test] fn trim_space_before_soft_break() { let original = "one \ntwo"; let expected = "

one\ntwo

\n"; let mut s = String::new(); html::push_html(&mut s, Parser::new(original)); assert_eq!(expected, s); } // Can't easily use regression.txt due to newline normalization. #[test] fn issue_819() { let original = [ "# \\", "# \\\n", "# \\\n\n", "# \\\r\n", "# \\\r\n\r\n", "# \\\n\r\n", "# \\\r\n\n" ]; let expected = "

\\

"; for orig in original { let mut s = String::new(); html::push_html(&mut s, Parser::new(orig)); // Trailing newline doesn't matter. Just the actual HTML. assert_eq!(expected, s.trim_end_matches('\n')); } for orig in original { let mut s = String::new(); let mut opts = Options::empty(); opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); html::push_html(&mut s, Parser::new_ext(orig, opts)); // Trailing newline doesn't matter. Just the actual HTML. assert_eq!(expected, s.trim_end_matches('\n')); } } pulldown-cmark-0.10.3/tests/lib.rs000064400000000000000000000026571046102023000151340ustar 00000000000000#![cfg(feature = "html")] use pulldown_cmark::{Options, Parser}; mod suite; #[inline(never)] pub fn test_markdown_html( input: &str, output: &str, smart_punct: bool, metadata_blocks: bool, old_footnotes: bool, ) { let mut s = String::new(); let mut opts = Options::empty(); opts.insert(Options::ENABLE_TABLES); opts.insert(Options::ENABLE_STRIKETHROUGH); opts.insert(Options::ENABLE_TASKLISTS); if old_footnotes { opts.insert(Options::ENABLE_OLD_FOOTNOTES); } else { opts.insert(Options::ENABLE_FOOTNOTES); } if metadata_blocks { opts.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS); opts.insert(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS); } if smart_punct { opts.insert(Options::ENABLE_SMART_PUNCTUATION); } opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); let p = Parser::new_ext(input, opts); pulldown_cmark::html::push_html(&mut s, p); // normalizing the HTML using html5ever may hide actual errors // assert_eq!(html_standardize(output), html_standardize(&s)); assert_eq!(html_standardize(output), html_standardize(&s)); } fn html_standardize(s: &str) -> String { s.replace("
", "
") .replace("
", "
") .replace("
", "
") .replace("
", "
") // permit extra or missing line breaks only between tags .replace(">\n<", "><") } pulldown-cmark-0.10.3/tests/serde.rs000064400000000000000000000054111046102023000154570ustar 00000000000000#[cfg(feature = "serde")] mod tests { use std::convert::TryInto; use pulldown_cmark::CowStr; #[test] fn cow_str_to_str_round_trip_bincode() { for i in &[ CowStr::Borrowed("dssa"), CowStr::Borrowed(""), CowStr::Boxed("hello".to_owned().into_boxed_str()), CowStr::Boxed("".to_owned().into_boxed_str()), CowStr::Inlined("inline".try_into().unwrap()), CowStr::Inlined("".try_into().unwrap()), ] { let encoded = bincode::serialize(i).unwrap(); let decoded1: CowStr = bincode::deserialize(&encoded).unwrap(); let decoded2: String = bincode::deserialize(&encoded).unwrap(); let decoded3: &str = bincode::deserialize(&encoded).unwrap(); assert_eq!(&decoded1, i); assert_eq!(decoded2, i.as_ref()); assert_eq!(decoded3, i.as_ref()); } } #[test] fn cow_str_to_str_round_trip_json() { for i in &[ CowStr::Borrowed("dssa"), CowStr::Borrowed(""), CowStr::Boxed("hello".to_owned().into_boxed_str()), CowStr::Boxed("".to_owned().into_boxed_str()), CowStr::Inlined("inline".try_into().unwrap()), CowStr::Inlined("".try_into().unwrap()), ] { let encoded = serde_json::to_string(i).unwrap(); let decoded1: CowStr = serde_json::from_str(&encoded).unwrap(); let decoded2: String = serde_json::from_str(&encoded).unwrap(); let decoded3: &str = serde_json::from_str(&encoded).unwrap(); assert_eq!(&decoded1, i); assert_eq!(decoded2, i.as_ref()); assert_eq!(decoded3, i.as_ref()); } } #[test] fn str_to_cow_str_json() { let str = "a borrowed str"; let string = "a owned str".to_owned(); let encoded_str = serde_json::to_string(&str).unwrap(); let encoded_string = serde_json::to_string(&string).unwrap(); let decoded_str: CowStr = serde_json::from_str(&encoded_str).unwrap(); let decoded_string: CowStr = serde_json::from_str(&encoded_string).unwrap(); assert_eq!(decoded_str.as_ref(), str); assert_eq!(decoded_string.as_ref(), string); } #[test] fn str_to_cow_str_bincode() { let str = "a borrowed str"; let string = "a owned str".to_owned(); let encoded_str = bincode::serialize(&str).unwrap(); let encoded_string = bincode::serialize(&string).unwrap(); let decoded_str: CowStr = bincode::deserialize(&encoded_str).unwrap(); let decoded_string: CowStr = bincode::deserialize(&encoded_string).unwrap(); assert_eq!(decoded_str.as_ref(), str); assert_eq!(decoded_string.as_ref(), string); } } pulldown-cmark-0.10.3/tests/suite/footnotes.rs000064400000000000000000000622521046102023000175340ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn footnotes_test_1() { let original = r##"Lorem ipsum.[^a] [^missing] [^a]: Cool. "##; let expected = r##"

Lorem ipsum.1 [^missing]

1

Cool.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_2() { let original = r##"> This is the song that never ends.\ > Yes it goes on and on my friends.[^lambchops] > > [^lambchops]: "##; let expected = r##"

This is the song that never ends.
Yes it goes on and on my friends.1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_3() { let original = r##"Songs that simply loop are a popular way to annoy people. [^examples] [^examples]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) * [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) "##; let expected = r##"

Songs that simply loop are a popular way to annoy people. 1

1
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_4() { let original = r##"Songs that simply loop are a popular way to annoy people. [^examples] [^examples]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) * [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) "##; let expected = r##"

Songs that simply loop are a popular way to annoy people. 1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_5() { let original = r##"[^not-code] [^code] [^quote] [^not-quote] [^indented-quote] [^not-code]: not code [^code]: code [^quote]: > quote [^not-quote]: > external quote [^indented-quote]: > indented quote "##; let expected = r##"

1 2 3 4 5

1

not code

2
code
3

quote

4

external quote

5

indented quote

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_6() { let original = r##"[^ab] [^cd] [^ab]: a b [^cd]: c\ d "##; let expected = r##"

1 2

1

a b

2

c
d

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_7() { let original = r##"[^lorem]: If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research. I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp. [^ipsum]: If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research. I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp. "##; let expected = r##"
1

If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research.

I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp.

2

If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research.

I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_8() { let original = r##"[^ipsum]: How much wood would a woodchuck chuck. If a woodchuck could chuck wood. # Forms of entertainment that aren't childish "##; let expected = r##"
1

How much wood would a woodchuck chuck.

If a woodchuck could chuck wood.

Forms of entertainment that aren't childish

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_9() { let original = r##"Footnotes [^one] [^many]. [^one]: first paragraph inside footnote [^many]: first paragraph inside footnote second paragraph still inside footnote "##; let expected = r##"

Footnotes 1 2.

1

first paragraph inside footnote

2

first paragraph inside footnote

second paragraph still inside footnote

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_10() { let original = r##"> He's also really stupid. [^why] > > [^why]: Because your mamma! As such, we can guarantee that the non-childish forms of entertainment are probably more entertaining to adults, since, having had a whole childhood doing the childish ones, the non-childish ones are merely the ones that haven't gotten boring yet. "##; let expected = r##"

He's also really stupid. 1

1

Because your mamma!

As such, we can guarantee that the non-childish forms of entertainment are probably more entertaining to adults, since, having had a whole childhood doing the childish ones, the non-childish ones are merely the ones that haven't gotten boring yet.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_11() { let original = r##"Nested footnotes are considered poor style. [^a] [^xkcd] [^indent1] [^indent2] [^a]: This does not mean that footnotes cannot reference each other. [^b] [^b]: This means that a footnote definition cannot be directly inside another footnote definition. > This means that a footnote cannot be directly inside another footnote's body. [^e] > > [^e]: They can, however, be inside anything else. [^xkcd]: [The other kind of nested footnote is, however, considered poor style.](https://xkcd.com/1208/) [^indent1]: indent1 [^indent2]: indent2 "##; let expected = r##"

Nested footnotes are considered poor style. 1 2 3 4

1

This does not mean that footnotes cannot reference each other. 5

5

This means that a footnote definition cannot be directly inside another footnote definition.

This means that a footnote cannot be directly inside another footnote's body. 6

6

They can, however, be inside anything else.

3

indent1

4

indent2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_12() { let original = r##"[^foo] [^bar] [^foo]: [^bar]: 1 "##; let expected = r##"

1 2

1
2

1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_13() { let original = r##"[^Doh] Ray Me Fa So La Te Do! [^1] [^Doh]: I know. Wrong Doe. And it won't render right. [^1]: Common for people practicing music. "##; let expected = r##"

1 Ray Me Fa So La Te Do! 2

1

I know. Wrong Doe. And it won't render right.

2

Common for people practicing music.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_14() { let original = r##"Lorem ipsum.[^a] An unordered list before the footnotes: * Ipsum * Lorem [^a]: Cool. "##; let expected = r##"

Lorem ipsum.1

An unordered list before the footnotes:

  • Ipsum
  • Lorem
1

Cool.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_15() { let original = r##"Songs that simply loop are a popular way to annoy people. [^examples] [^examples]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) * [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) Songs that simply loop are a popular way to annoy people. [^examples2] [^examples2]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) 2 * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) 2 - [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) 2 Songs that simply loop are a popular way to annoy people. [^examples3] [^examples3]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) 3 * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) 3 * [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) 3 "##; let expected = r##"

Songs that simply loop are a popular way to annoy people. 1

Songs that simply loop are a popular way to annoy people. 2

Songs that simply loop are a popular way to annoy people. 3

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_16() { let original = r##"My [cmark-gfm][^c]. My [cmark-gfm][cmark-gfm][^c]. My [cmark-gfm][][^c]. My [cmark-gfm] [^c]. My [cmark-gfm[^c]]. [cmark-gfm]: https://github.com/github/cmark-gfm/blob/1e230827a584ebc9938c3eadc5059c55ef3c9abf/test/extensions.txt#L702 [^c]: cmark-gfm is under the MIT license, so incorporating parts of its test suite into pulldown-cmark should be fine. My [otherlink[^c]]. [otherlink[^c]]: https://github.com/github/cmark-gfm/blob/1e230827a584ebc9938c3eadc5059c55ef3c9abf/test/extensions.txt#L702 "##; let expected = r##"

My [cmark-gfm]1.

My cmark-gfm1.

My cmark-gfm1.

My cmark-gfm 1.

My [cmark-gfm1].

1

cmark-gfm is under the MIT license, so incorporating parts of its test suite into pulldown-cmark should be fine.

My [otherlink1].

[otherlink1]: https://github.com/github/cmark-gfm/blob/1e230827a584ebc9938c3eadc5059c55ef3c9abf/test/extensions.txt#L702

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_17() { let original = r##"[^1]: footnote definition text // indented code block fn main() { println!("hello world!"); } "##; let expected = r##"
1

footnote definition text

// indented code block
fn main() {
    println!("hello world!");
}
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_18() { let original = r##"[^1]: footnote definition text [^1]\: this is a reference, rather than a definition "##; let expected = r##"
1

footnote definition text 1: this is a reference, rather than a definition

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_19() { let original = r##"[^1]: | column1 | column2 | |---------|---------| | row1a | row1b | | row2a | row2b | "##; let expected = r##"
1
column1column2
row1arow1b
row2arow2b
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_20() { let original = r##"* First [^1]: test * Second [^1] test > first > [^2]: test > Second [^2] test First | Second -----------|---------- first | second [^3]: test | test [^3] | First | Second | |------------|-----------| | first | second | | [^4]: test | test [^4] | > [^5]: * test [^5] "##; let expected = r##"
  • First
    1

    test

  • Second 1 test

first

2

test Second 2 test

FirstSecond
firstsecond
3

test | test 3

FirstSecond
firstsecond
[^4]: testtest [^4]
4
  • test 4
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_21() { let original = r##"Test [^] link [^]: https://rust-lang.org "##; let expected = r##"

Test ^ link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_22() { let original = r##"[^foo\ bar]: not a footnote definition [baz\ quux]: https://rust-lang.org [first second]: https://rust-lang.org [^third fourth]: not a footnote definition [baz\ quux] [^foo\ bar] [first second] [^third fourth] "##; let expected = r##"

[^foo
bar]: not a footnote definition

[^third fourth]: not a footnote definition

baz
quux
[^foo
bar] first second [^third fourth]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_23() { let original = r##"[^foo ]: https://rust-lang.org [^foo ] "##; let expected = r##"

^foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn footnotes_test_24() { let original = r##"footnote [^baz] footnote [^quux] [^quux]: x [^baz]: x "##; let expected = r##"

footnote 1 footnote [^quux]

[^quux]: x
1

x

"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/gfm_strikethrough.rs000064400000000000000000000015021046102023000212360ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn gfm_strikethrough_test_1() { let original = r##"~~Hi~~ Hello, ~there~ world! "##; let expected = r##"

Hi Hello, there world!

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_strikethrough_test_2() { let original = r##"This ~~has a new paragraph~~. "##; let expected = r##"

This ~~has a

new paragraph~~.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_strikethrough_test_3() { let original = r##"This will ~~~not~~~ strike. "##; let expected = r##"

This will ~~~not~~~ strike.

"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/gfm_table.rs000064400000000000000000000062231046102023000174300ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn gfm_table_test_1() { let original = r##"| foo | bar | | --- | --- | | baz | bim | "##; let expected = r##"
foo bar
baz bim
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_2() { let original = r##"| abc | defghi | :-: | -----------: bar | baz "##; let expected = r##"
abc defghi
bar baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_3() { let original = r##"| f\|oo | | ------ | | b `\|` az | | b **\|** im | "##; let expected = r##"
f|oo
b | az
b | im
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_4() { let original = r##"| abc | def | | --- | --- | | bar | baz | > bar "##; let expected = r##"
abc def
bar baz

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_5() { let original = r##"| abc | def | | --- | --- | | bar | baz | bar bar "##; let expected = r##"
abc def
bar baz
bar

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_6() { let original = r##"| abc | def | | --- | | bar | "##; let expected = r##"

| abc | def | | --- | | bar |

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_7() { let original = r##"| abc | def | | --- | --- | | bar | | bar | baz | boo | "##; let expected = r##"
abc def
bar
bar baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_8() { let original = r##"| abc | def | | --- | --- | "##; let expected = r##"
abc def
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_table_test_9() { let original = r##"Hello World | abc | def | | --- | --- | | bar | baz | "##; let expected = r##"

Hello World

abc def
bar baz
"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/gfm_tasklist.rs000064400000000000000000000015471046102023000202030ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn gfm_tasklist_test_1() { let original = r##"- [ ] foo - [x] bar "##; let expected = r##"
  • foo
  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn gfm_tasklist_test_2() { let original = r##"- [x] foo - [ ] bar - [x] baz - [ ] bim "##; let expected = r##"
  • foo
    • bar
    • baz
  • bim
"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/heading_attrs.rs000064400000000000000000000302231046102023000203210ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn heading_attrs_test_1() { let original = r##"with the ID {#myh1} =================== with a class {.myclass} ------------ with a custom attribute {myattr=myvalue} ======================================== multiple! {.myclass1 myattr #myh3 otherattr=value .myclass2} -- "##; let expected = r##"

with the ID

with a class

with a custom attribute

multiple!

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_2() { let original = r##"# with the ID {#myh1} ## with a class {.myclass} #### with a custom attribute {myattr=myvalue} ### multiple! {.myclass1 myattr #myh3 otherattr=value .myclass2} "##; let expected = r##"

with the ID

with a class

with a custom attribute

multiple!

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_3() { let original = r##"# H1 # {#id1} ## H2 ## with ## multiple ## hashes ## {#id2} ### with trailing hash # ### {#id3} #### non-attribute-block {#id4} #### "##; let expected = r##"

H1

H2 ## with ## multiple ## hashes

with trailing hash #

non-attribute-block {#id4}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_4() { let original = r##"# spaces {#myid1} ## tabs {#myid2} "##; let expected = r##"

spaces

tabs

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_5() { let original = r##"# H1 \ nextline "##; let expected = r##"

H1 \

nextline

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_6() { let original = r##"# H1 \ {#myid} ## H2 \ nextline {.class} ### H3 [link ](https://example.com/) {#myid3} "##; let expected = r##"

H1 \

{#myid}

H2 \

nextline {.class}

H3 [link

](https://example.com/) {#myid3}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_7() { let original = r##"H1 cont {#myid} == "##; let expected = r##"

H1 cont

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_8() { let original = r##"H1 { .class1 .class2 } == "##; let expected = r##"

H1 { .class1 .class2 }

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_9() { let original = r##"# without space, not recommended{#id1} ## recommended style with spaces {#id2} "##; let expected = r##"

without space, not recommended

recommended style with spaces

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_10() { let original = r##"# H1 { #id1 } ## H2 {.myclass #id2 } ### H3 { .myclass} "##; let expected = r##"

H1

H2

H3

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_11() { let original = r##"# H1 {#id1.class1.class2 .class3} ## H2 {.class1#id2.class2} "##; let expected = r##"

H1

H2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_12() { let original = r##"# H1 { #id1 ## H2 {#id2 "##; let expected = r##"

H1 { #id1

H2 {#id2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_13() { let original = r##"# H1 #id1 } ## H2 #id2} "##; let expected = r##"

H1 #id1 }

H2 #id2}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_14() { let original = r##"# H1 { #id1 } foo ## H2 {#id2} "##; let expected = r##"

H1 { #id1 } foo

H2 {#id2}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_15() { let original = r##"# *H1* { #id1 } ## **H2** {#id2} ### _H3_ {#id3} #### ~~H4~~ {#id4} ##### [text](uri) {#id5} "##; let expected = r##"

H1

H2

H3

H4

text
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_16() { let original = r##"# H1 {#first #second #last} "##; let expected = r##"

H1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_17() { let original = r##"# H1 {.z .a .zz} "##; let expected = r##"

H1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_18() { let original = r##"# H1 {.a .a .a} "##; let expected = r##"

H1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_19() { let original = r##"# H1 {.myclass #myid} ## H2 {.z #m .a} "##; let expected = r##"

H1

H2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_20() { let original = r##"# H1 {foo} ## H2 {#myid unknown this#is.ignored attr=value .myclass} "##; let expected = r##"

H1

H2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_21() { let original = r##"# Header # {myattr=value other_attr} "##; let expected = r##"

Header

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_22() { let original = r##"#### Header {#id myattr= .class1 other_attr=false} "##; let expected = r##"

Header

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_23() { let original = r##"# H1 {.foo{unknown} ## H2 {.foo{.bar} "##; let expected = r##"

H1 {.foo

H2 {.foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_24() { let original = r##"# H1 {.foo}bar} "##; let expected = r##"

H1 {.foo}bar}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_25() { let original = r##"# H1 {foo} "##; let expected = r##"

H1 {foo}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_26() { let original = r##"# H1 {.foo\} "##; let expected = r##"

H1 {.foo}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_27() { let original = r##"H1 {.foo .bar} == "##; let expected = r##"

H1 {.foo .bar}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_28() { let original = r##"H1 {} {} ===== ## H2 {} {} "##; let expected = r##"

H1 {}

H2 {}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_29() { let original = r##"## H2 {} ## "##; let expected = r##"

H2 {}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_30() { let original = r##"# H1 {\} ## this is also ok \{\} newline can be used for setext heading { } -- "##; let expected = r##"

H1 {}

this is also ok {}

newline can be used for setext heading { }

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_31() { let original = r##"# H1 \{.foo} ## H2 \\{.bar} ### stray backslash at the end is preserved \ "##; let expected = r##"

H1 \

H2 \

stray backslash at the end is preserved \

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_32() { let original = r##"H1 \{.foo} == H2 \\{.bar} -- stray backslash at the end is preserved \ -- "##; let expected = r##"

H1 \

H2 \

stray backslash at the end is preserved \

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_33() { let original = r##"# H1 {#`code`} ## H2 {#foo__bar__baz} ### H3 {#foo**bar**baz} "##; let expected = r##"

H1

H2

H3

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_34() { let original = r##"H1 {#`code`} == H2-1 {#foo__bar__baz} ---- H2-2 {#foo**bar**baz} -- "##; let expected = r##"

H1

H2-1

H2-2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_35() { let original = r##"# H1 {.foo#bar} ## H2 {#foo.bar} ### H3 {.a"b'c&d} "##; let expected = r##"

H1

H2

H3

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_36() { let original = r##"# H1 {#} ## H2 {.} "##; let expected = r##"

H1

H2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_37() { let original = r##"# H1 {#foo #} # H1 {.foo . . .bar} "##; let expected = r##"

H1

H1

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_38() { let original = r##"# {} ## {} ### {\} #### {} {} #{} "##; let expected = r##"

{}

{}

#{}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_39() { let original = r##"{} == \{} -- \ -- {\} == {}{} -- "##; let expected = r##"

\

\

{}

{}

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_40() { let original = r##"# horizontal tab # horizontal tab {#ht} ## form feed ## form feed {#ff} ### vertical tab ### vertical tab {#vt} "##; let expected = r##"

horizontal tab

horizontal tab

form feed

form feed

vertical tab

vertical tab

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_41() { let original = r##"# horizontal tab (U+000A) {#ht .myclass} ## form feed (U+000C) {#ff .myclass} # vertical tab (U+000B) {#vt .myclass} "##; let expected = r##"

horizontal tab (U+000A)

form feed (U+000C)

vertical tab (U+000B)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn heading_attrs_test_42() { let original = r##"# EN SPACE (U+2002) {#en-space .myclass} ## IDEOGRAPHIC SPACE (U+3000) {#ideographic-space .myclass} "##; let expected = r##"

EN SPACE (U+2002)

IDEOGRAPHIC SPACE (U+3000)

"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/metadata_blocks.rs000064400000000000000000000056071046102023000206320ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn metadata_blocks_test_1() { let original = r##"--- title: example another_field: 0 --- "##; let expected = r##""##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_2() { let original = r##"--- title: example another_field: 0 "##; let expected = r##"

title: example another_field: 0

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_3() { let original = r##"--- --- "##; let expected = r##"

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_4() { let original = r##"--- title: example another_field: 0 --- "##; let expected = r##"

title: example another_field: 0

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_5() { let original = r##"My paragraph here. --- title: example another_field: 0 --- "##; let expected = r##"

My paragraph here.

title: example another_field: 0

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_6() { let original = r##"My paragraph here. --- title: example another_field: 0 --- "##; let expected = r##"

My paragraph here.

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_7() { let original = r##"--- title: example another_field: 0 --- --- - title: example another_field: 0 --- "##; let expected = r##"

title: example another_field: 0

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_8() { let original = r##"--- title: example another_field: 0 --- --- title: example another_field: 0 ---a "##; let expected = r##"

title: example another_field: 0 ---a

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_9() { let original = r##"--- title: example another_field: 0 ... "##; let expected = r##""##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_10() { let original = r##"+++ title: example another_field: 0 +++ "##; let expected = r##""##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_11() { let original = r##" --- Things --- "##; let expected = r##"
---
Things
---
"##; test_markdown_html(original, expected, false, true, false); } #[test] fn metadata_blocks_test_12() { let original = r##"--- - Item 1 - Item 2 --- "##; let expected = r##""##; test_markdown_html(original, expected, false, true, false); } pulldown-cmark-0.10.3/tests/suite/mod.rs000064400000000000000000000005101046102023000162600ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually pub use super::test_markdown_html; mod footnotes; mod gfm_strikethrough; mod gfm_table; mod gfm_tasklist; mod heading_attrs; mod metadata_blocks; mod old_footnotes; mod regression; mod smart_punct; mod spec; mod strikethrough; mod table; pulldown-cmark-0.10.3/tests/suite/old_footnotes.rs000064400000000000000000000224231046102023000203660ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn old_footnotes_test_1() { let original = r##"Lorem ipsum.[^a] [^a]: Cool. "##; let expected = r##"

Lorem ipsum.1

1

Cool.

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_2() { let original = r##"> This is the song that never ends.\ > Yes it goes on and on my friends.[^lambchops] > > [^lambchops]: "##; let expected = r##"

This is the song that never ends.
Yes it goes on and on my friends.1

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_3() { let original = r##"Songs that simply loop are a popular way to annoy people. [^examples] [^examples]: * [The song that never ends](https://www.youtube.com/watch?v=0U2zJOryHKQ) * [I know a song that gets on everybody's nerves](https://www.youtube.com/watch?v=TehWI09qxls) * [Ninety-nine bottles of beer on the wall](https://www.youtube.com/watch?v=qVjCag8XoHQ) "##; let expected = r##"

Songs that simply loop are a popular way to annoy people. 1

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_4() { let original = r##"[^lorem]: If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research. I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp. "##; let expected = r##"
1

If heaven ever wishes to grant me a boon, it will be a total effacing of the results of a mere chance which fixed my eye on a certain stray piece of shelf-paper. It was nothing on which I would naturally have stumbled in the course of my daily round, for it was an old number of an Australian journal, the Sydney Bulletin for April 18, 1925. It had escaped even the cutting bureau which had at the time of its issuance been avidly collecting material for my uncle's research.

I had largely given over my inquiries into what Professor Angell called the "Cthulhu Cult", and was visiting a learned friend in Paterson, New Jersey; the curator of a local museum and a mineralogist of note. Examining one day the reserve specimens roughly set on the storage shelves in a rear room of the museum, my eye was caught by an odd picture in one of the old papers spread beneath the stones. It was the Sydney Bulletin I have mentioned, for my friend had wide affiliations in all conceivable foreign parts; and the picture was a half-tone cut of a hideous stone image almost identical with that which Legrasse had found in the swamp.

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_5() { let original = r##"[^ipsum]: How much wood would a woodchuck chuck. If a woodchuck could chuck wood. # Forms of entertainment that aren't childish "##; let expected = r##"
1

How much wood would a woodchuck chuck.

If a woodchuck could chuck wood.

Forms of entertainment that aren't childish

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_6() { let original = r##"> He's also really stupid. [^why] > > [^why]: Because your mamma! As such, we can guarantee that the non-childish forms of entertainment are probably more entertaining to adults, since, having had a whole childhood doing the childish ones, the non-childish ones are merely the ones that haven't gotten boring yet. "##; let expected = r##"

He's also really stupid. 1

1

Because your mamma!

As such, we can guarantee that the non-childish forms of entertainment are probably more entertaining to adults, since, having had a whole childhood doing the childish ones, the non-childish ones are merely the ones that haven't gotten boring yet.

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_7() { let original = r##"Nested footnotes are considered poor style. [^a] [^xkcd] [^a]: This does not mean that footnotes cannot reference each other. [^b] [^b]: This means that a footnote definition cannot be directly inside another footnote definition. > This means that a footnote cannot be directly inside another footnote's body. [^e] > > [^e]: They can, however, be inside anything else. [^xkcd]: [The other kind of nested footnote is, however, considered poor style.](https://xkcd.com/1208/) "##; let expected = r##"

Nested footnotes are considered poor style. 1 2

1

This does not mean that footnotes cannot reference each other. 3

3

This means that a footnote definition cannot be directly inside another footnote definition.

This means that a footnote cannot be directly inside another footnote's body. 4

4

They can, however, be inside anything else.

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_8() { let original = r##"[^Doh] Ray Me Fa So La Te Do! [^1] [^Doh]: I know. Wrong Doe. And it will render right. [^1]: Common for people practicing music. "##; let expected = r##"

1 Ray Me Fa So La Te Do! 2

1

I know. Wrong Doe. And it will render right. 2: Common for people practicing music.

"##; test_markdown_html(original, expected, false, false, true); } #[test] fn old_footnotes_test_9() { let original = r##"[Reference to footnotes A[^1], B[^2] and C[^3]. [^1]: Footnote A. [^2]: Footnote B. [^3]: Footnote C. "##; let expected = r##"

[Reference to footnotes A1, B2 and C3.

1

Footnote A.

2

Footnote B.

3

Footnote C.

"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/regression.rs000064400000000000000000001554721046102023000177030ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn regression_test_1() { let original = r##"
Testing 1..2..3.. This is a test of the details element.
"##; let expected = r##"
Testing 1..2..3..

This is a test of the details element.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_2() { let original = r##"see the [many] [articles] [on] [QuickCheck]. [many]: https://medium.com/@jlouis666/quickcheck-advice-c357efb4e7e6 [articles]: http://www.quviq.com/products/erlang-quickcheck/ [on]: https://wiki.haskell.org/Introduction_to_QuickCheck1 [QuickCheck]: https://hackage.haskell.org/package/QuickCheck "##; let expected = r##"

see the many articles on QuickCheck.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_3() { let original = r##"[![debug-stub-derive on crates.io][cratesio-image]][cratesio] [![debug-stub-derive on docs.rs][docsrs-image]][docsrs] [cratesio-image]: https://img.shields.io/crates/v/debug_stub_derive.svg [cratesio]: https://crates.io/crates/debug_stub_derive [docsrs-image]: https://docs.rs/debug_stub_derive/badge.svg?version=0.3.0 [docsrs]: https://docs.rs/debug_stub_derive/0.3.0/ "##; let expected = r##"

debug-stub-derive on crates.io debug-stub-derive on docs.rs

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_4() { let original = r##"| Title A | Title B | | --------- | --------- | | Content | Content | | Title A | Title B | Title C | Title D | | --------- | --------- | --------- | ---------:| | Content | Content | Conent | Content | "##; let expected = r##"
Title ATitle B
ContentContent
Title ATitle BTitle CTitle D
ContentContentConentContent
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_5() { let original = r##"foo§__(bar)__ "##; let expected = r##"

foo§(bar)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_6() { let original = r##" hello "##; let expected = r##"

https://example.com hello

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_7() { let original = r##"[foo][bar] [bar]: a "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_8() { let original = r##" - **foo** (u8, u8) make something - **bar** (u16, u16) make something "##; let expected = r##"
  • foo (u8, u8)

    make something

  • bar (u16, u16)

    make something

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_9() { let original = r##"[` i8 `]( ../../../std/primitive.i8.html ) "##; let expected = r##"

i8

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_10() { let original = r##"[a] [a]: /url (title\\*) "##; let expected = r##"

a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_11() { let original = r##"[a] [a]: /url (title\)) "##; let expected = r##"

a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_12() { let original = r##"[a] [a]: /url (title)) "##; let expected = r##"

[a]

[a]: /url (title))

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_13() { let original = r##"a "##; let expected = r##"

a <?php this is not a valid processing tag

b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_14() { let original = r##"[a]: u\ foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_15() { let original = r##"\`foo` "##; let expected = r##"

`foo`

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_16() { let original = r##"foo\\ bar "##; let expected = r##"

foo\ bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_17() { let original = r##"1\. foo 1\) bar "##; let expected = r##"

1. foo

1) bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_18() { let original = r##"1... 1.2.3. 1 2 3 . 1.|2.-3. 1)2)3) "##; let expected = r##"

1...

1.2.3.

1 2 3 .

1.|2.-3.

1)2)3)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_19() { let original = r##"[](<<>) "##; let expected = r##"

[](<<>)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_20() { let original = r##"\``foo``bar` "##; let expected = r##"

`foo``bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_21() { let original = r##"\\`foo` "##; let expected = r##"

\foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_22() { let original = r##"[\\]: x YOLO "##; let expected = r##"

YOLO

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_23() { let original = r##"lorem ipsum A | B ---|--- foo | bar "##; let expected = r##"

lorem ipsum A | B ---|--- foo | bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_24() { let original = r##"foo|bar ---|--- foo|bar "##; let expected = r##"
foobar
foobar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_25() { let original = r##"foo|bar\\ ---|--- foo|bar "##; let expected = r##"
foobar\
foobar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_26() { let original = r##"[](url) "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_27() { let original = r##"[bar](url) "##; let expected = r##"

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_28() { let original = r##"![](http://example.com/logo.png) "##; let expected = r##"

http://example.com

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_29() { let original = r##"[ ](url) "##; let expected = r##"

http://one http://two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_30() { let original = r##"Markdown | Less | Pretty --- | --- | --- some text "##; let expected = r##"
MarkdownLessPretty

some text

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_31() { let original = r##"1. > foo 2. > "##; let expected = r##"
  1. foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_32() { let original = r##"[ x ]: f "##; let expected = r##"

[ x

]: f

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_33() { let original = r##"[foo]: "##; let expected = r##"

[foo]:

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_34() { let original = r##"> [foo > bar]: /url > > [foo bar] "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_35() { let original = r##"> foo | bar > --- | --- yolo | swag "##; let expected = r##"
foobar

yolo | swag

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_36() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_37() { let original = r##" "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_38() { let original = r##"~~*_**__ __a__ "##; let expected = r##"

~~*_**__

a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_39() { let original = r##"> ` > ` "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_40() { let original = r##"`\|` "##; let expected = r##"

\|

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_41() { let original = r##"Paragraph 1 Paragraph 2 "##; let expected = r##"

Paragraph 1

Paragraph 2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_42() { let original = r##"\[[link text](https://www.google.com/)\] "##; let expected = r##"

[link text]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_43() { let original = r##"foo | bar --- | --- [a](< | url>) "##; let expected = r##"
foobar
[a](<url>)
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_44() { let original = r##"[a](url " - - - ") "##; let expected = r##"

[a](url "


")

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_45() { let original = r##"[a](url ) "##; let expected = r##"

[a](url

)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_46() { let original = r##"[a](b " ") "##; let expected = r##"

[a](b "

")

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_47() { let original = r##" "##; let expected = r##"

<http:// >

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_48() { let original = r##" "##; let expected = r##"

<http://>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_49() { let original = r##"foo | bar --- | --- foobar <http://baz "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_50() { let original = r##"foo | bar --- | --- "##; let expected = r##"
foobar
<http://>
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_51() { let original = r##"\*hi\_ "##; let expected = r##"

*hi_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_52() { let original = r##"email: \_ "##; let expected = r##"

email: john@example.com_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_53() { let original = r##"> [link](/url 'foo > bar') "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_54() { let original = r##"> [foo > bar]: /url > > [foo bar] "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_55() { let original = r##"> [foo bar]: /url > > [foo > bar] "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_56() { let original = r##"> - [a > b c]: /foo [a b c] "##; let expected = r##"

a b c

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_57() { let original = r##"[a > b]: /foo [a b] [a > b] "##; let expected = r##"

[a

b]: /foo

[a b] [a > b]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_58() { let original = r##"[`cargo package`] [`cargo package`]: https://example.com "##; let expected = r##"

cargo package

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_59() { let original = r##"> [`cargo > package`] [`cargo package`]: https://example.com "##; let expected = r##"

cargo package

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_60() { let original = r##"> `cargo > package` "##; let expected = r##"

cargo package

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_61() { let original = r##"> Note: Though you should not rely on this, all pointers to title="Dynamically Sized Types">DSTs are currently twice the size of > the size of `usize` and have the same alignment. "##; let expected = r##"

Note: Though you should not rely on this, all pointers to DSTs are currently twice the size of the size of usize and have the same alignment.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_62() { let original = r##"Lorem ipsum.[^a] An unordered list before the footnotes: * Ipsum * Lorem [^a]: Cool. "##; let expected = r##"

Lorem ipsum.1

An unordered list before the footnotes:

  • Ipsum
  • Lorem
1

Cool.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_63() { let original = r##"[][a] [a]: b # assimp-rs [![][crates-badge]][crates] [crates]: https://crates.io/crates/assimp [crates-badge]: http://meritbadge.herokuapp.com/assimp "##; let expected = r##"

assimp-rs

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_64() { let original = r##"* A list. * A sublist. * Another sublist. * A list. * A sublist. * Another sublist. "##; let expected = r##"
  • A list.

    • A sublist.

    • Another sublist.

  • A list.

    • A sublist.

    • Another sublist.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_65() { let original = r##"<foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_66() { let original = r##"> > a > ="yo > > lo"> "##; let expected = r##"

a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_67() { let original = r##" - the whitespace here are tabs "##; let expected = r##"
-	the whitespace here are tabs
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_68() { let original = r##"1. a 1. a a 2. a "##; let expected = r##"
  1. a
    1. a

a 2. a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_69() { let original = r##"1. a 2. a 2. a "##; let expected = r##"
  1. a
  2. a 2. a
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_70() { let original = r##"* [ ] foo * [ ] bar baz "##; let expected = r##"
  • foo

  • bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_71() { let original = r##"* foo + bar + baz "##; let expected = r##"
  • foo
    • bar
    • baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_72() { let original = r##"[`]: xx: [`]`] "##; let expected = r##"

[]]

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

foobar

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

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_75() { let original = r##"*~~__emphasis strike strong__~~* ~~*__strike emphasis strong__*~~ "##; let expected = r##"

emphasis strike strong strike emphasis strong

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_76() { let original = r##"*~~__emphasis strike strong__~~* ~~*__`strike emphasis strong code`__*~~ "##; let expected = r##"

emphasis strike strong strike emphasis strong code

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_77() { let original = r##"*~~`emphasis strike code`~~* ~~*__strike emphasis strong__*~~ "##; let expected = r##"

emphasis strike code strike emphasis strong

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_78() { let original = r##"*~~`emphasis strike code`~~* ~~*__`strike emphasis strong code`__*~~ "##; let expected = r##"

emphasis strike code strike emphasis strong code

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_79() { let original = r##"**~~_strong strike emphasis_~~** ~~*__strike emphasis strong__*~~ "##; let expected = r##"

strong strike emphasis strike emphasis strong

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_80() { let original = r##"**~~_strong strike emphasis_~~** ~~*__`strike emphasis strong code`__*~~ "##; let expected = r##"

strong strike emphasis strike emphasis strong code

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_81() { let original = r##"**~~`strong strike code`~~** ~~*__strike emphasis strong__*~~ "##; let expected = r##"

strong strike code strike emphasis strong

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_82() { let original = r##"**~~`strong strike code`~~** ~~*__`strike emphasis strong code`__*~~ "##; let expected = r##"

strong strike code strike emphasis strong code

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_83() { let original = r##" | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | | foo | bar | |-----|------| | baz | alef | "##; let expected = r##"
foobar
bazalef
foobar
bazalef
foobar
bazalef
foobar
bazalef
foobar
bazalef
foobar
bazalef
foobar
bazalef
foobar
bazalef
| foo | bar  |
|-----|------|
| baz | alef |

| foo | bar | |-----|------| | baz | alef |

| foo | bar  |
|-----|------|
| baz | alef |

| foo | bar | |-----|------| | baz | alef |

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_84() { let original = r##"### ### "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_85() { let original = r##"### "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_86() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_87() { let original = r##"| A | table | | ------ | ---- | | not | in | | a | list| * A blockquote: > inside a list item * A Heading: # inside a list item * A table: | inside | a | | ------ | ------- | | list | item | | with | leading | | empty | lines | * A table: | inside | a | | ------- | ------- | | list | item | | without | leading | | empty | lines | "##; let expected = r##"
Atable
notin
alist
  • A blockquote:

    inside a list item

  • A Heading:

    inside a list item

  • A table:

    insidea
    listitem
    withleading
    emptylines
  • A table:

    insidea
    listitem
    withoutleading
    emptylines
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_88() { let original = r##"a\ b "##; let expected = r##"

a\

b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_89() { let original = r##"a\ * b "##; let expected = r##"

a\

  • b
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_90() { let original = r##"a\ > b "##; let expected = r##"

a\

b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_91() { let original = r##"a\ # b "##; let expected = r##"

a\

b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_92() { let original = r##"a\ == "##; let expected = r##"

a\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_93() { let original = r##"> a\ > "##; let expected = r##"

a\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_94() { let original = r##"
"##; let expected = r##"

<a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_95() { let original = r##"
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_96() { let original = r##" quote "##; let expected = r##"

<a

quote

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_97() { let original = r##"
not quote "##; let expected = r##"
not quote "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_98() { let original = r##"quote "##; let expected = r##"

<a

quote

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_99() { let original = r##"
not quote "##; let expected = r##"
not quote "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_100() { let original = r##"> alpha > | a | b | > |---|---| > | c | d | "##; let expected = r##"

alpha

ab
cd
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_101() { let original = r##"***R]*-* "##; let expected = r##"

*R]-

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_102() { let original = r##"****foo*bar*baz**** "##; let expected = r##"

foobarbaz**

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_103() { let original = r##"; * % "##; let expected = r##"

; * %

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_104() { let original = r##"; * % "##; let expected = r##"

; * %

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_105() { let original = r##"<@1> "##; let expected = r##"

<@1>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_106() { let original = r##"--- anything: indented4Spaces: true --- Things "##; let expected = r##"

Things

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn regression_test_107() { let original = r##"--- something: nested: twice: true --- Things "##; let expected = r##"

Things

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn regression_test_108() { let original = r##"--- lists: - indented 4 spaces --- Things "##; let expected = r##"

Things

"##; test_markdown_html(original, expected, false, true, false); } #[test] fn regression_test_109() { let original = r##"- - - - "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_110() { let original = r##"- - . "##; let expected = r##"
  • .

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_111() { let original = r##"j***5*=* "##; let expected = r##"

j*5=

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_112() { let original = r##"Not enough table | | Not enough table |x | Not enough table | |- Table |x |- Not enough table | col1 | col2 | | | ---- | Not enough table | col1 | col2 | | : | ---- | Table | col1 | col2 | | ---- | ---- | "##; let expected = r##"

Not enough table

| |

Not enough table

|x |

Not enough table

| |-

Table

x

Not enough table | col1 | col2 | | | ---- |

Not enough table | col1 | col2 | | : | ---- |

Table

col1col2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_113() { let original = r##"[x] is not a valid link definition, because parens aren't balanced. [x]: ( "##; let expected = r##"

[x] is not a valid link definition, because parens aren't balanced.

[x]: (

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_114() { let original = r##"Both of these two paragraphs are structurally the same, but the first one has an unmatched asterisk. _*_ *{*{ _x_ *{*{ "##; let expected = r##"

Both of these two paragraphs are structurally the same, but the first one has an unmatched asterisk.

* {{

x {{

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_115() { let original = r##"**a.*.**a*.**. "##; let expected = r##"

*a.*.a..

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_116() { let original = r##"_*__*_* _*xx*_* _*__-_- _*xx-_- "##; let expected = r##"

__*

xx*

*__--

*xx--

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_117() { let original = r##"- - - x - "##; let expected = r##"
-
  • x

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_118() { let original = r##"- - - x - "##; let expected = r##"
-
  • x

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_119() { let original = r##"[x\ ]: https://rust-lang.org "##; let expected = r##"

[x\

]: https://rust-lang.org

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_120() { let original = r##">>**#* > >**#* **#* |**#*| |----| |**#*| |**#* **#*| **#* "##; let expected = r##"

*#

*#

*#

*#
*#
*#
*#
*#
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_121() { let original = r##"The second hyphen should parse the same way in both samples. - >* - The second hyphen should parse the same way in both samples. - >x - "##; let expected = r##"

The second hyphen should parse the same way in both samples.

The second hyphen should parse the same way in both samples.

  • x

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_122() { let original = r##"> Rewriting it in [Rust] is usually a bad idea. > > [Rust]: https://rust-lang.org "##; let expected = r##"

Rewriting it in Rust is usually a bad idea.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_123() { let original = r##"[First try ---------- Second try]: https://rust-lang.org "##; let expected = r##"

[First try

Second try]: https://rust-lang.org

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_124() { let original = r##"[^foo][] [^foo][baz] [baz][^foo] [^foo]: bar [baz]: https://rust-lang.org "##; let expected = r##"

1[]

^foo

[baz]1

1

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_125() { let original = r##"# foo \ bar \ "##; let expected = r##"

foo \

bar \

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_126() { let original = r##"[third try]: - [third try] "##; let expected = r##"

[third try]:

[third try]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_127() { let original = r##"- [foo]: test bar > [bar]: test [baz]: rstr [bar] "##; let expected = r##"
  • bar

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_128() { let original = r##"> - [foo]: test > bar > > > [bar]: test > [baz]: rstr > [bar] "##; let expected = r##"
  • bar

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_129() { let original = r##"[bar]: test - "##; let expected = r##"

-

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_130() { let original = r##"[link]( foo) > [link]( > foo) "##; let expected = r##"

link

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_131() { let original = r##"[link](foo ) > [link](foo > ) "##; let expected = r##"

link

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_132() { let original = r##"[link](foo "bar" ) > [link](foo > "bar" > ) "##; let expected = r##"

link

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_133() { let original = r##"[link]( foo "bar" ) > [link]( > foo > "bar" > ) "##; let expected = r##"

link

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_134() { let original = r##"[linkme]: foo - baz "##; let expected = r##"

- baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_135() { let original = r##"[linkme-3]: [^foo]: [linkme-4]: [^bar]: GFM footnotes can interrupt link defs if they have three spaces, but not four. "##; let expected = r##"

[linkme-3]:

1

GFM footnotes can interrupt link defs if they have three spaces, but not four.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_136() { let original = r##"[linkme-3]: === [linkme-4]: === Setext heading can interrupt link def if it has three spaces, but not four. "##; let expected = r##"

[linkme-3]:

Setext heading can interrupt link def if it has three spaces, but not four.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_137() { let original = r##"[linkme-3]: a - a [linkme-4]: a - b List can interrupt the paragraph at the start of a link definition if it starts with three spaces, but not four. "##; let expected = r##"
  • a

- b

List can interrupt the paragraph at the start of a link definition if it starts with three spaces, but not four.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_138() { let original = r##"[first - second]: https://example.com [first - second] "##; let expected = r##"

[first

second]: https://example.com

[first

second]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_139() { let original = r##"[first - second]: https://example.com [first - second] "##; let expected = r##"

first - second

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_140() { let original = r##"[first]: https://example.com " - " [first] "##; let expected = r##"

"

"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_141() { let original = r##"[first]: https://example.com " - " [first] "##; let expected = r##"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_142() { let original = r##"> [first]: https://example.com > " > - > " > > [first] "##; let expected = r##"

"

"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_143() { let original = r##"> ``` > code > text "##; let expected = r##"
code

text

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_144() { let original = r##"> [first]: https://example.com > " > - > " > > [first] "##; let expected = r##"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_145() { let original = r##"[first]: https://example.com " \ " [first] "##; let expected = r##"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_146() { let original = r##"[first]: https://example.com " \ " [first] "##; let expected = r##"

" \

"

first

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_147() { let original = r##"[foo]: https://example.com '[foo]'bar "##; let expected = r##"

'foo'bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_148() { let original = r##"- [foo]: https://example.com '[foo]' [foo] "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_149() { let original = r##"[ a]: https://example.com [b ]: https://example.com [a] [b] "##; let expected = r##"

a b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_150() { let original = r##"> > ``` > > code > > > text "##; let expected = r##"
code

text

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_151() { let original = r##"- *foo - - baz* "##; let expected = r##"
  • *foo
    baz*
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_152() { let original = r##"- `foo - - baz` "##; let expected = r##"
  • `foo
    baz`
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_153() { let original = r##"- [foo - - baz](https://example.com) "##; let expected = r##"
  • [foo
    baz](https://example.com)
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_154() { let original = r##"[mylink] [mylink]: https://example.com ' part of the title' "##; let expected = r##"

mylink

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_155() { let original = r##"1. pulldown-cmark used to think this was code, but commonmark.js and commonmark-hs didn't. The dot in the list marker is at column two, so the child paragraph actually starts in column *three*. "##; let expected = r##"
  1. pulldown-cmark used to think this was code, but commonmark.js and commonmark-hs didn't. The dot in the list marker is at column two, so the child paragraph actually starts in column three.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_156() { let original = r##"1. This is not in the list at all. It's a paragraph after it. "##; let expected = r##"

This is not in the list at all. It's a paragraph after it.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_157() { let original = r##"`\!\"\#\$\%\& \!\"\#\$\%\& \!\"\#\$\%\&` "##; let expected = r##"

\!\&quot;\#\$\%\& \!\&quot;\#\$\%\& \!\&quot;\#\$\%\&

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_158() { let original = r##"| -|- * "##; let expected = r##"

| -|- *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_159() { let original = r##" A paragraph is a paragraph and spaces must be removed. Another paragraph whose spaces must be removed. "##; let expected = r##"

A paragraph is a paragraph and spaces must be removed.

Another paragraph whose spaces must be removed.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_160() { let original = r##"![^1] [^1]: foo "##; let expected = r##"

!1

1

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_161() { let original = r##"First ![^1][] Second [^1]: foo "##; let expected = r##"

First !1[] Second

1

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_162() { let original = r##"� � "##; let expected = r##"

&#00000000; &#x0000000;

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_163() { let original = r##"� "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_164() { let original = r##"- _t # test t_ "##; let expected = r##"
  • _t

    test

    t_
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_165() { let original = r##"* *_ # N* "##; let expected = r##"
  • *_

    N*
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_166() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_167() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_168() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_169() { let original = r##"* "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_170() { let original = r##"* test "##; let expected = r##"
  • test

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_171() { let original = r##"*
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_172() { let original = r##"*
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_173() { let original = r##"*
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_174() { let original = r##"*
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_175() { let original = r##"*
"##; let expected = r##"
  • <div>
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_176() { let original = r##"[link]: test (() [link] "##; let expected = r##"

[link]: test (()

[link]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_177() { let original = r##"[link]: test (()) [link] "##; let expected = r##"

[link]: test (())

[link]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_178() { let original = r##"[link]: test (\(\)) [link] "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_179() { let original = r##"[link]: test """ [link] "##; let expected = r##"

[link]: test """

[link]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_180() { let original = r##"[link]: test "\"" [link] "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_181() { let original = r##"[link]: test ''' [link] "##; let expected = r##"

[link]: test '''

[link]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_182() { let original = r##"[link]: test '\'' [link] "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_183() { let original = r##"- test test2 "##; let expected = r##"
  • test
    

    test2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_184() { let original = r##" test test2 "##; let expected = r##"
test

test2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_185() { let original = r##"- test test2 "##; let expected = r##"
  • test
    
    test2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_186() { let original = r##"- test - test2 "##; let expected = r##"
  • test
    
  • test2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_187() { let original = r##"- test - test2 "##; let expected = r##"
  • test
    
  • test2

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_188() { let original = r##" <\!p> "##; let expected = r##"

<!p>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_189() { let original = r##"[linky] [linky]: ((())) "##; let expected = r##"

linky

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_190() { let original = r##">junk "##; let expected = r##"

<span title junk

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_191() { let original = r##" ~~~ ~~~ "##; let expected = r##"
   ~~~
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_192() { let original = r##" ~~~ ~~~ "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_193() { let original = r##"[link]: destination " text " [link] "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn regression_test_194() { let original = r##"* _ _** ___ ^_ "##; let expected = r##"
  • _ _**
    ^_
"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/smart_punct.rs000064400000000000000000000103361046102023000200470ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn smart_punct_test_1() { let original = r##""Hello," said the spider. "'Shelob' is my name." "##; let expected = r##"

“Hello,” said the spider. “‘Shelob’ is my name.”

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_2() { let original = r##"'A', 'B', and 'C' are letters. "##; let expected = r##"

‘A’, ‘B’, and ‘C’ are letters.

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_3() { let original = r##"'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' "##; let expected = r##"

‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_4() { let original = r##"'He said, "I want to go."' "##; let expected = r##"

‘He said, “I want to go.”’

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_5() { let original = r##"Were you alive in the 70's? "##; let expected = r##"

Were you alive in the 70’s?

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_6() { let original = r##"Here is some quoted '`code`' and a "[quoted link](url)". "##; let expected = r##"

Here is some quoted ‘code’ and a “quoted link”.

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_7() { let original = r##"'tis the season to be 'jolly' "##; let expected = r##"

’tis the season to be ‘jolly’

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_8() { let original = r##"'We'll use Jane's boat and John's truck,' Jenna said. "##; let expected = r##"

‘We’ll use Jane’s boat and John’s truck,’ Jenna said.

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_9() { let original = r##""A paragraph with no closing quote. "Second paragraph by same speaker, in fiction." "##; let expected = r##"

“A paragraph with no closing quote.

“Second paragraph by same speaker, in fiction.”

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_10() { let original = r##"[a]'s b' "##; let expected = r##"

[a]’s b’

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_11() { let original = r##"\"This is not smart.\" This isn\'t either. 5\'8\" "##; let expected = r##"

"This is not smart." This isn't either. 5'8"

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_12() { let original = r##"Some dashes: em---em en--en em --- em en -- en 2--3 "##; let expected = r##"

Some dashes: em—em en–en em — em en – en 2–3

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_13() { let original = r##"one- two-- three--- four---- five----- six------ seven------- eight-------- nine--------- thirteen-------------. "##; let expected = r##"

one- two– three— four–– five—– six—— seven—–– eight–––– nine——— thirteen———––.

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_14() { let original = r##"Escaped hyphens: \-- \-\-\-. "##; let expected = r##"

Escaped hyphens: -- ---.

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_15() { let original = r##"Ellipses...and...and.... "##; let expected = r##"

Ellipses…and…and….

"##; test_markdown_html(original, expected, true, false, false); } #[test] fn smart_punct_test_16() { let original = r##"No ellipses\.\.\. "##; let expected = r##"

No ellipses...

"##; test_markdown_html(original, expected, true, false, false); } pulldown-cmark-0.10.3/tests/suite/spec.rs000064400000000000000000004265551046102023000164600ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn spec_test_1() { let original = r##" foo baz bim "##; let expected = r##"
foo	baz		bim
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_2() { let original = r##" foo baz bim "##; let expected = r##"
foo	baz		bim
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_3() { let original = r##" a a ὐ a "##; let expected = r##"
a	a
ὐ	a
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_4() { let original = r##" - foo bar "##; let expected = r##"
  • foo

    bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_5() { let original = r##"- foo bar "##; let expected = r##"
  • foo

      bar
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_6() { let original = r##"> foo "##; let expected = r##"
  foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_7() { let original = r##"- foo "##; let expected = r##"
  •   foo
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_8() { let original = r##" foo bar "##; let expected = r##"
foo
bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_9() { let original = r##" - foo - bar - baz "##; let expected = r##"
  • foo
    • bar
      • baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_10() { let original = r##"# Foo "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_11() { let original = r##"* * * "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_12() { let original = r##"\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~ "##; let expected = r##"

!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_13() { let original = r##"\ \A\a\ \3\φ\« "##; let expected = r##"

\ \A\a\ \3\φ\«

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_14() { let original = r##"\*not emphasized* \
not a tag \[not a link](/foo) \`not code` 1\. not a list \* not a list \# not a heading \[foo]: /url "not a reference" \ö not a character entity "##; let expected = r##"

*not emphasized* <br/> not a tag [not a link](/foo) `not code` 1. not a list * not a list # not a heading [foo]: /url "not a reference" &ouml; not a character entity

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_15() { let original = r##"\\*emphasis* "##; let expected = r##"

\emphasis

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_16() { let original = r##"foo\ bar "##; let expected = r##"

foo
bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_17() { let original = r##"`` \[\` `` "##; let expected = r##"

\[\`

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_18() { let original = r##" \[\] "##; let expected = r##"
\[\]
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_19() { let original = r##"~~~ \[\] ~~~ "##; let expected = r##"
\[\]
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_20() { let original = r##" "##; let expected = r##"

https://example.com?find=\*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_21() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_22() { let original = r##"[foo](/bar\* "ti\*tle") "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_23() { let original = r##"[foo] [foo]: /bar\* "ti\*tle" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_24() { let original = r##"``` foo\+bar foo ``` "##; let expected = r##"
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_25() { let original = r##"  & © Æ Ď ¾ ℋ ⅆ ∲ ≧̸ "##; let expected = r##"

  & © Æ Ď ¾ ℋ ⅆ ∲ ≧̸

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_26() { let original = r##"# Ӓ Ϡ � "##; let expected = r##"

# Ӓ Ϡ �

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_27() { let original = r##"" ആ ಫ "##; let expected = r##"

" ആ ಫ

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_28() { let original = r##"  &x; &#; &#x; � &#abcdef0; &ThisIsNotDefined; &hi?; "##; let expected = r##"

&nbsp &x; &#; &#x; &#87654321; &#abcdef0; &ThisIsNotDefined; &hi?;

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_29() { let original = r##"© "##; let expected = r##"

&copy

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_30() { let original = r##"&MadeUpEntity; "##; let expected = r##"

&MadeUpEntity;

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_31() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_32() { let original = r##"[foo](/föö "föö") "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_33() { let original = r##"[foo] [foo]: /föö "föö" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_34() { let original = r##"``` föö foo ``` "##; let expected = r##"
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_35() { let original = r##"`föö` "##; let expected = r##"

f&ouml;&ouml;

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_36() { let original = r##" föfö "##; let expected = r##"
f&ouml;f&ouml;
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_37() { let original = r##"*foo* *foo* "##; let expected = r##"

*foo* foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_38() { let original = r##"* foo * foo "##; let expected = r##"

* foo

  • foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_39() { let original = r##"foo bar "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_40() { let original = r##" foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_41() { let original = r##"[a](url "tit") "##; let expected = r##"

[a](url "tit")

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_42() { let original = r##"- `one - two` "##; let expected = r##"
  • `one
  • two`
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_43() { let original = r##"*** --- ___ "##; let expected = r##"


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

+++

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_45() { let original = r##"=== "##; let expected = r##"

===

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_46() { let original = r##"-- ** __ "##; let expected = r##"

-- ** __

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_47() { let original = r##" *** *** *** "##; let expected = r##"


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_48() { let original = r##" *** "##; let expected = r##"
***
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_49() { let original = r##"Foo *** "##; let expected = r##"

Foo ***

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_50() { let original = r##"_____________________________________ "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_51() { let original = r##" - - - "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_52() { let original = r##" ** * ** * ** * ** "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_53() { let original = r##"- - - - "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_54() { let original = r##"- - - - "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_55() { let original = r##"_ _ _ _ a a------ ---a--- "##; let expected = r##"

_ _ _ _ a

a------

---a---

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_56() { let original = r##" *-* "##; let expected = r##"

-

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_57() { let original = r##"- foo *** - bar "##; let expected = r##"
  • foo

  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_58() { let original = r##"Foo *** bar "##; let expected = r##"

Foo


bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_59() { let original = r##"Foo --- bar "##; let expected = r##"

Foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_60() { let original = r##"* Foo * * * * Bar "##; let expected = r##"
  • Foo

  • Bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_61() { let original = r##"- Foo - * * * "##; let expected = r##"
  • Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_62() { let original = r##"# foo ## foo ### foo #### foo ##### foo ###### foo "##; let expected = r##"

foo

foo

foo

foo

foo
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_63() { let original = r##"####### foo "##; let expected = r##"

####### foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_64() { let original = r##"#5 bolt #hashtag "##; let expected = r##"

#5 bolt

#hashtag

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_65() { let original = r##"\## foo "##; let expected = r##"

## foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_66() { let original = r##"# foo *bar* \*baz\* "##; let expected = r##"

foo bar *baz*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_67() { let original = r##"# foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_68() { let original = r##" ### foo ## foo # foo "##; let expected = r##"

foo

foo

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_69() { let original = r##" # foo "##; let expected = r##"
# foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_70() { let original = r##"foo # bar "##; let expected = r##"

foo # bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_71() { let original = r##"## foo ## ### bar ### "##; let expected = r##"

foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_72() { let original = r##"# foo ################################## ##### foo ## "##; let expected = r##"

foo

foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_73() { let original = r##"### foo ### "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_74() { let original = r##"### foo ### b "##; let expected = r##"

foo ### b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_75() { let original = r##"# foo# "##; let expected = r##"

foo#

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_76() { let original = r##"### foo \### ## foo #\## # foo \# "##; let expected = r##"

foo ###

foo ###

foo #

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_77() { let original = r##"**** ## foo **** "##; let expected = r##"

foo


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_78() { let original = r##"Foo bar # baz Bar foo "##; let expected = r##"

Foo bar

baz

Bar foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_79() { let original = r##"## # ### ### "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_80() { let original = r##"Foo *bar* ========= Foo *bar* --------- "##; let expected = r##"

Foo bar

Foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_81() { let original = r##"Foo *bar baz* ==== "##; let expected = r##"

Foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_82() { let original = r##" Foo *bar baz* ==== "##; let expected = r##"

Foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_83() { let original = r##"Foo ------------------------- Foo = "##; let expected = r##"

Foo

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_84() { let original = r##" Foo --- Foo ----- Foo === "##; let expected = r##"

Foo

Foo

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_85() { let original = r##" Foo --- Foo --- "##; let expected = r##"
Foo
---

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_86() { let original = r##"Foo ---- "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_87() { let original = r##"Foo --- "##; let expected = r##"

Foo ---

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_88() { let original = r##"Foo = = Foo --- - "##; let expected = r##"

Foo = =

Foo


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_89() { let original = r##"Foo ----- "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_90() { let original = r##"Foo\ ---- "##; let expected = r##"

Foo\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_91() { let original = r##"`Foo ---- ` "##; let expected = r##"

`Foo

`

<a title="a lot

of dashes"/>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_92() { let original = r##"> Foo --- "##; let expected = r##"

Foo


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_93() { let original = r##"> foo bar === "##; let expected = r##"

foo bar ===

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_94() { let original = r##"- Foo --- "##; let expected = r##"
  • Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_95() { let original = r##"Foo Bar --- "##; let expected = r##"

Foo Bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_96() { let original = r##"--- Foo --- Bar --- Baz "##; let expected = r##"

Foo

Bar

Baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_97() { let original = r##" ==== "##; let expected = r##"

====

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_98() { let original = r##"--- --- "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_99() { let original = r##"- foo ----- "##; let expected = r##"
  • foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_100() { let original = r##" foo --- "##; let expected = r##"
foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_101() { let original = r##"> foo ----- "##; let expected = r##"

foo


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_102() { let original = r##"\> foo ------ "##; let expected = r##"

> foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_103() { let original = r##"Foo bar --- baz "##; let expected = r##"

Foo

bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_104() { let original = r##"Foo bar --- baz "##; let expected = r##"

Foo bar


baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_105() { let original = r##"Foo bar * * * baz "##; let expected = r##"

Foo bar


baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_106() { let original = r##"Foo bar \--- baz "##; let expected = r##"

Foo bar --- baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_107() { let original = r##" a simple indented code block "##; let expected = r##"
a simple
  indented code block
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_108() { let original = r##" - foo bar "##; let expected = r##"
  • foo

    bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_109() { let original = r##"1. foo - bar "##; let expected = r##"
  1. foo

    • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_110() { let original = r##"
*hi* - one "##; let expected = r##"
<a/>
*hi*

- one
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_111() { let original = r##" chunk1 chunk2 chunk3 "##; let expected = r##"
chunk1

chunk2



chunk3
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_112() { let original = r##" chunk1 chunk2 "##; let expected = r##"
chunk1
  
  chunk2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_113() { let original = r##"Foo bar "##; let expected = r##"

Foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_114() { let original = r##" foo bar "##; let expected = r##"
foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_115() { let original = r##"# Heading foo Heading ------ foo ---- "##; let expected = r##"

Heading

foo

Heading

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_116() { let original = r##" foo bar "##; let expected = r##"
    foo
bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_117() { let original = r##" foo "##; let expected = r##"
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_118() { let original = r##" foo "##; let expected = r##"
foo  
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_119() { let original = r##"``` < > ``` "##; let expected = r##"
<
 >
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_120() { let original = r##"~~~ < > ~~~ "##; let expected = r##"
<
 >
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_121() { let original = r##"`` foo `` "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_122() { let original = r##"``` aaa ~~~ ``` "##; let expected = r##"
aaa
~~~
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_123() { let original = r##"~~~ aaa ``` ~~~ "##; let expected = r##"
aaa
```
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_124() { let original = r##"```` aaa ``` `````` "##; let expected = r##"
aaa
```
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_125() { let original = r##"~~~~ aaa ~~~ ~~~~ "##; let expected = r##"
aaa
~~~
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_126() { let original = r##"``` "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_127() { let original = r##"````` ``` aaa "##; let expected = r##"

```
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_128() { let original = r##"> ``` > aaa bbb "##; let expected = r##"
aaa

bbb

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

  
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_130() { let original = r##"``` ``` "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_131() { let original = r##" ``` aaa aaa ``` "##; let expected = r##"
aaa
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_132() { let original = r##" ``` aaa aaa aaa ``` "##; let expected = r##"
aaa
aaa
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_133() { let original = r##" ``` aaa aaa aaa ``` "##; let expected = r##"
aaa
 aaa
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_134() { let original = r##" ``` aaa ``` "##; let expected = r##"
```
aaa
```
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_135() { let original = r##"``` aaa ``` "##; let expected = r##"
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_136() { let original = r##" ``` aaa ``` "##; let expected = r##"
aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_137() { let original = r##"``` aaa ``` "##; let expected = r##"
aaa
    ```
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_138() { let original = r##"``` ``` aaa "##; let expected = r##"

aaa

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_139() { let original = r##"~~~~~~ aaa ~~~ ~~ "##; let expected = r##"
aaa
~~~ ~~
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_140() { let original = r##"foo ``` bar ``` baz "##; let expected = r##"

foo

bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_141() { let original = r##"foo --- ~~~ bar ~~~ # baz "##; let expected = r##"

foo

bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_142() { let original = r##"```ruby def foo(x) return 3 end ``` "##; let expected = r##"
def foo(x)
  return 3
end
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_143() { let original = r##"~~~~ ruby startline=3 $%@#$ def foo(x) return 3 end ~~~~~~~ "##; let expected = r##"
def foo(x)
  return 3
end
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_144() { let original = r##"````; ```` "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_145() { let original = r##"``` aa ``` foo "##; let expected = r##"

aa foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_146() { let original = r##"~~~ aa ``` ~~~ foo ~~~ "##; let expected = r##"
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_147() { let original = r##"``` ``` aaa ``` "##; let expected = r##"
``` aaa
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_148() { let original = r##"
**Hello**,

_world_.
"##; let expected = r##"
**Hello**,

world.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_149() { let original = r##"
hi
okay. "##; let expected = r##"
hi

okay.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_150() { let original = r##"
*foo* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_152() { let original = r##"
*Markdown*
"##; let expected = r##"

Markdown

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_153() { let original = r##"
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_154() { let original = r##"
"##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_155() { let original = r##"
*foo* *bar* "##; let expected = r##"
*foo*

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_156() { let original = r##"
"##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_160() { let original = r##"
foo
"##; let expected = r##"
foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_161() { let original = r##"
``` c int x = 33; ``` "##; let expected = r##"
``` c int x = 33; ``` "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_162() { let original = r##" *bar* "##; let expected = r##" *bar* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_163() { let original = r##" *bar* "##; let expected = r##" *bar* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_164() { let original = r##" *bar* "##; let expected = r##" *bar* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_165() { let original = r##" *bar* "##; let expected = r##" *bar* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_166() { let original = r##" *foo* "##; let expected = r##" *foo* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_167() { let original = r##" *foo* "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_168() { let original = r##"*foo* "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_169() { let original = r##"

import Text.HTML.TagSoup

main :: IO ()
main = print $ parseTags tags
okay "##; let expected = r##"

import Text.HTML.TagSoup

main :: IO ()
main = print $ parseTags tags

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_170() { let original = r##" okay "##; let expected = r##"

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_171() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_172() { let original = r##" okay "##; let expected = r##"

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_173() { let original = r##" *foo* "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_177() { let original = r##"*bar* *baz* "##; let expected = r##"*bar*

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_178() { let original = r##"1. *bar* "##; let expected = r##"1. *bar* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_179() { let original = r##" okay "##; let expected = r##"

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_180() { let original = r##"'; ?> okay "##; let expected = r##"'; ?>

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_181() { let original = r##" "##; let expected = r##" "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_182() { let original = r##" okay "##; let expected = r##"

okay

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_183() { let original = r##" "##; let expected = r##"
<!-- foo -->
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_184() { let original = r##"
"##; let expected = r##"
<div>
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_185() { let original = r##"Foo
bar
"##; let expected = r##"

Foo

bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_186() { let original = r##"
bar
*foo* "##; let expected = r##"
bar
*foo* "##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_187() { let original = r##"Foo baz "##; let expected = r##"

Foo baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_188() { let original = r##"
*Emphasized* text.
"##; let expected = r##"

Emphasized text.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_189() { let original = r##"
*Emphasized* text.
"##; let expected = r##"
*Emphasized* text.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_190() { let original = r##"
Hi
"##; let expected = r##"
Hi
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_191() { let original = r##"
Hi
"##; let expected = r##"
<td>
  Hi
</td>
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_192() { let original = r##"[foo]: /url "title" [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_193() { let original = r##" [foo]: /url 'the title' [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_194() { let original = r##"[Foo*bar\]]:my_(url) 'title (with parens)' [Foo*bar\]] "##; let expected = r##"

Foo*bar]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_195() { let original = r##"[Foo bar]: 'title' [Foo bar] "##; let expected = r##"

Foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_196() { let original = r##"[foo]: /url ' title line1 line2 ' [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_197() { let original = r##"[foo]: /url 'title with blank line' [foo] "##; let expected = r##"

[foo]: /url 'title

with blank line'

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_198() { let original = r##"[foo]: /url [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_199() { let original = r##"[foo]: [foo] "##; let expected = r##"

[foo]:

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_200() { let original = r##"[foo]: <> [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_201() { let original = r##"[foo]: (baz) [foo] "##; let expected = r##"

[foo]: (baz)

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_202() { let original = r##"[foo]: /url\bar\*baz "foo\"bar\baz" [foo] "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_203() { let original = r##"[foo] [foo]: url "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_204() { let original = r##"[foo] [foo]: first [foo]: second "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_205() { let original = r##"[FOO]: /url [Foo] "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_206() { let original = r##"[ΑΓΩ]: /φου [αγω] "##; let expected = r##"

αγω

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_207() { let original = r##"[foo]: /url "##; let expected = r##""##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_208() { let original = r##"[ foo ]: /url bar "##; let expected = r##"

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_209() { let original = r##"[foo]: /url "title" ok "##; let expected = r##"

[foo]: /url "title" ok

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_210() { let original = r##"[foo]: /url "title" ok "##; let expected = r##"

"title" ok

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_211() { let original = r##" [foo]: /url "title" [foo] "##; let expected = r##"
[foo]: /url "title"

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_212() { let original = r##"``` [foo]: /url ``` [foo] "##; let expected = r##"
[foo]: /url

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_213() { let original = r##"Foo [bar]: /baz [bar] "##; let expected = r##"

Foo [bar]: /baz

[bar]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_214() { let original = r##"# [Foo] [foo]: /url > bar "##; let expected = r##"

Foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_215() { let original = r##"[foo]: /url bar === [foo] "##; let expected = r##"

bar

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_216() { let original = r##"[foo]: /url === [foo] "##; let expected = r##"

=== foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_217() { let original = r##"[foo]: /foo-url "foo" [bar]: /bar-url "bar" [baz]: /baz-url [foo], [bar], [baz] "##; let expected = r##"

foo, bar, baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_218() { let original = r##"[foo] > [foo]: /url "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_219() { let original = r##"aaa bbb "##; let expected = r##"

aaa

bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_220() { let original = r##"aaa bbb ccc ddd "##; let expected = r##"

aaa bbb

ccc ddd

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_221() { let original = r##"aaa bbb "##; let expected = r##"

aaa

bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_222() { let original = r##" aaa bbb "##; let expected = r##"

aaa bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_223() { let original = r##"aaa bbb ccc "##; let expected = r##"

aaa bbb ccc

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_224() { let original = r##" aaa bbb "##; let expected = r##"

aaa bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_225() { let original = r##" aaa bbb "##; let expected = r##"
aaa

bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_226() { let original = r##"aaa bbb "##; let expected = r##"

aaa
bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_227() { let original = r##" aaa # aaa "##; let expected = r##"

aaa

aaa

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_228() { let original = r##"> # Foo > bar > baz "##; let expected = r##"

Foo

bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_229() { let original = r##"># Foo >bar > baz "##; let expected = r##"

Foo

bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_230() { let original = r##" > # Foo > bar > baz "##; let expected = r##"

Foo

bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_231() { let original = r##" > # Foo > bar > baz "##; let expected = r##"
> # Foo
> bar
> baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_232() { let original = r##"> # Foo > bar baz "##; let expected = r##"

Foo

bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_233() { let original = r##"> bar baz > foo "##; let expected = r##"

bar baz foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_234() { let original = r##"> foo --- "##; let expected = r##"

foo


"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_235() { let original = r##"> - foo - bar "##; let expected = r##"
  • foo
  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_236() { let original = r##"> foo bar "##; let expected = r##"
foo
bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_237() { let original = r##"> ``` foo ``` "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_238() { let original = r##"> foo - bar "##; let expected = r##"

foo - bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_239() { let original = r##"> "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_240() { let original = r##"> > > "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_241() { let original = r##"> > foo > "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_242() { let original = r##"> foo > bar "##; let expected = r##"

foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_243() { let original = r##"> foo > bar "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_244() { let original = r##"> foo > > bar "##; let expected = r##"

foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_245() { let original = r##"foo > bar "##; let expected = r##"

foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_246() { let original = r##"> aaa *** > bbb "##; let expected = r##"

aaa


bbb

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_247() { let original = r##"> bar baz "##; let expected = r##"

bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_248() { let original = r##"> bar baz "##; let expected = r##"

bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_249() { let original = r##"> bar > baz "##; let expected = r##"

bar

baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_250() { let original = r##"> > > foo bar "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_251() { let original = r##">>> foo > bar >>baz "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_252() { let original = r##"> code > not code "##; let expected = r##"
code

not code

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_253() { let original = r##"A paragraph with two lines. indented code > A block quote. "##; let expected = r##"

A paragraph with two lines.

indented code

A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_254() { let original = r##"1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
  1. A paragraph with two lines.

    indented code
    

    A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_255() { let original = r##"- one two "##; let expected = r##"
  • one

two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_256() { let original = r##"- one two "##; let expected = r##"
  • one

    two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_257() { let original = r##" - one two "##; let expected = r##"
  • one
 two
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_258() { let original = r##" - one two "##; let expected = r##"
  • one

    two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_259() { let original = r##" > > 1. one >> >> two "##; let expected = r##"
  1. one

    two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_260() { let original = r##">>- one >> > > two "##; let expected = r##"
  • one

two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_261() { let original = r##"-one 2.two "##; let expected = r##"

-one

2.two

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_262() { let original = r##"- foo bar "##; let expected = r##"
  • foo

    bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_263() { let original = r##"1. foo ``` bar ``` baz > bam "##; let expected = r##"
  1. foo

    bar
    

    baz

    bam

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_264() { let original = r##"- Foo bar baz "##; let expected = r##"
  • Foo

    bar
    
    
    baz
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_265() { let original = r##"123456789. ok "##; let expected = r##"
  1. ok
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_266() { let original = r##"1234567890. not ok "##; let expected = r##"

1234567890. not ok

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_267() { let original = r##"0. ok "##; let expected = r##"
  1. ok
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_268() { let original = r##"003. ok "##; let expected = r##"
  1. ok
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_269() { let original = r##"-1. not ok "##; let expected = r##"

-1. not ok

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_270() { let original = r##"- foo bar "##; let expected = r##"
  • foo

    bar
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_271() { let original = r##" 10. foo bar "##; let expected = r##"
  1. foo

    bar
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_272() { let original = r##" indented code paragraph more code "##; let expected = r##"
indented code

paragraph

more code
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_273() { let original = r##"1. indented code paragraph more code "##; let expected = r##"
  1. indented code
    

    paragraph

    more code
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_274() { let original = r##"1. indented code paragraph more code "##; let expected = r##"
  1.  indented code
    

    paragraph

    more code
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_275() { let original = r##" foo bar "##; let expected = r##"

foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_276() { let original = r##"- foo bar "##; let expected = r##"
  • foo

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_277() { let original = r##"- foo bar "##; let expected = r##"
  • foo

    bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_278() { let original = r##"- foo - ``` bar ``` - baz "##; let expected = r##"
  • foo
  • bar
    
  • baz
    
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_279() { let original = r##"- foo "##; let expected = r##"
  • foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_280() { let original = r##"- foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_281() { let original = r##"- foo - - bar "##; let expected = r##"
  • foo
  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_282() { let original = r##"- foo - - bar "##; let expected = r##"
  • foo
  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_283() { let original = r##"1. foo 2. 3. bar "##; let expected = r##"
  1. foo
  2. bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_284() { let original = r##"* "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_285() { let original = r##"foo * foo 1. "##; let expected = r##"

foo *

foo 1.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_286() { let original = r##" 1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
  1. A paragraph with two lines.

    indented code
    

    A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_287() { let original = r##" 1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
  1. A paragraph with two lines.

    indented code
    

    A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_288() { let original = r##" 1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
  1. A paragraph with two lines.

    indented code
    

    A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_289() { let original = r##" 1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
1.  A paragraph
    with two lines.

        indented code

    > A block quote.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_290() { let original = r##" 1. A paragraph with two lines. indented code > A block quote. "##; let expected = r##"
  1. A paragraph with two lines.

    indented code
    

    A block quote.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_291() { let original = r##" 1. A paragraph with two lines. "##; let expected = r##"
  1. A paragraph with two lines.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_292() { let original = r##"> 1. > Blockquote continued here. "##; let expected = r##"
  1. Blockquote continued here.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_293() { let original = r##"> 1. > Blockquote > continued here. "##; let expected = r##"
  1. Blockquote continued here.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_294() { let original = r##"- foo - bar - baz - boo "##; let expected = r##"
  • foo
    • bar
      • baz
        • boo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_295() { let original = r##"- foo - bar - baz - boo "##; let expected = r##"
  • foo
  • bar
  • baz
  • boo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_296() { let original = r##"10) foo - bar "##; let expected = r##"
  1. foo
    • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_297() { let original = r##"10) foo - bar "##; let expected = r##"
  1. foo
  • bar
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_298() { let original = r##"- - foo "##; let expected = r##"
    • foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_299() { let original = r##"1. - 2. foo "##; let expected = r##"
      1. foo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_300() { let original = r##"- # Foo - Bar --- baz "##; let expected = r##"
  • Foo

  • Bar

    baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_301() { let original = r##"- foo - bar + baz "##; let expected = r##"
  • foo
  • bar
  • baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_302() { let original = r##"1. foo 2. bar 3) baz "##; let expected = r##"
  1. foo
  2. bar
  1. baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_303() { let original = r##"Foo - bar - baz "##; let expected = r##"

Foo

  • bar
  • baz
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_304() { let original = r##"The number of windows in my house is 14. The number of doors is 6. "##; let expected = r##"

The number of windows in my house is 14. The number of doors is 6.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_305() { let original = r##"The number of windows in my house is 1. The number of doors is 6. "##; let expected = r##"

The number of windows in my house is

  1. The number of doors is 6.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_306() { let original = r##"- foo - bar - baz "##; let expected = r##"
  • foo

  • bar

  • baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_307() { let original = r##"- foo - bar - baz bim "##; let expected = r##"
  • foo
    • bar
      • baz

        bim

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_308() { let original = r##"- foo - bar - baz - bim "##; let expected = r##"
  • foo
  • bar
  • baz
  • bim
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_309() { let original = r##"- foo notcode - foo code "##; let expected = r##"
  • foo

    notcode

  • foo

code
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_310() { let original = r##"- a - b - c - d - e - f - g "##; let expected = r##"
  • a
  • b
  • c
  • d
  • e
  • f
  • g
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_311() { let original = r##"1. a 2. b 3. c "##; let expected = r##"
  1. a

  2. b

  3. c

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_312() { let original = r##"- a - b - c - d - e "##; let expected = r##"
  • a
  • b
  • c
  • d - e
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_313() { let original = r##"1. a 2. b 3. c "##; let expected = r##"
  1. a

  2. b

3. c
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_314() { let original = r##"- a - b - c "##; let expected = r##"
  • a

  • b

  • c

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_315() { let original = r##"* a * * c "##; let expected = r##"
  • a

  • c

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_316() { let original = r##"- a - b c - d "##; let expected = r##"
  • a

  • b

    c

  • d

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_317() { let original = r##"- a - b [ref]: /url - d "##; let expected = r##"
  • a

  • b

  • d

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_318() { let original = r##"- a - ``` b ``` - c "##; let expected = r##"
  • a
  • b
    
    
    
  • c
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_319() { let original = r##"- a - b c - d "##; let expected = r##"
  • a
    • b

      c

  • d
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_320() { let original = r##"* a > b > * c "##; let expected = r##"
  • a

    b

  • c
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_321() { let original = r##"- a > b ``` c ``` - d "##; let expected = r##"
  • a

    b

    c
    
  • d
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_322() { let original = r##"- a "##; let expected = r##"
  • a
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_323() { let original = r##"- a - b "##; let expected = r##"
  • a
    • b
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_324() { let original = r##"1. ``` foo ``` bar "##; let expected = r##"
  1. foo
    

    bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_325() { let original = r##"* foo * bar baz "##; let expected = r##"
  • foo

    • bar

    baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_326() { let original = r##"- a - b - c - d - e - f "##; let expected = r##"
  • a

    • b
    • c
  • d

    • e
    • f
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_327() { let original = r##"`hi`lo` "##; let expected = r##"

hilo`

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_328() { let original = r##"`foo` "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_329() { let original = r##"`` foo ` bar `` "##; let expected = r##"

foo ` bar

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

``

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

``

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_332() { let original = r##"` a` "##; let expected = r##"

a

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_333() { let original = r##"` b ` "##; let expected = r##"

 b 

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

 

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_335() { let original = r##"`` foo bar baz `` "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_336() { let original = r##"`` foo `` "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_337() { let original = r##"`foo bar baz` "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_338() { let original = r##"`foo\`bar` "##; let expected = r##"

foo\bar`

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_339() { let original = r##"``foo`bar`` "##; let expected = r##"

foo`bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_340() { let original = r##"` foo `` bar ` "##; let expected = r##"

foo `` bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_341() { let original = r##"*foo`*` "##; let expected = r##"

*foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_342() { let original = r##"[not a `link](/foo`) "##; let expected = r##"

[not a link](/foo)

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

<a href="">`

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

`

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

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

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

https://foo.bar.`baz`

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_347() { let original = r##"```foo`` "##; let expected = r##"

```foo``

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_348() { let original = r##"`foo "##; let expected = r##"

`foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_349() { let original = r##"`foo``bar`` "##; let expected = r##"

`foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_350() { let original = r##"*foo bar* "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_351() { let original = r##"a * foo bar* "##; let expected = r##"

a * foo bar*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_352() { let original = r##"a*"foo"* "##; let expected = r##"

a*"foo"*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_353() { let original = r##"* a * "##; let expected = r##"

* a *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_354() { let original = r##"*$*alpha. *£*bravo. *€*charlie. "##; let expected = r##"

*$*alpha.

*£*bravo.

*€*charlie.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_355() { let original = r##"foo*bar* "##; let expected = r##"

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_356() { let original = r##"5*6*78 "##; let expected = r##"

5678

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_357() { let original = r##"_foo bar_ "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_358() { let original = r##"_ foo bar_ "##; let expected = r##"

_ foo bar_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_359() { let original = r##"a_"foo"_ "##; let expected = r##"

a_"foo"_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_360() { let original = r##"foo_bar_ "##; let expected = r##"

foo_bar_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_361() { let original = r##"5_6_78 "##; let expected = r##"

5_6_78

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_362() { let original = r##"пристаням_стремятся_ "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_363() { let original = r##"aa_"bb"_cc "##; let expected = r##"

aa_"bb"_cc

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_364() { let original = r##"foo-_(bar)_ "##; let expected = r##"

foo-(bar)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_365() { let original = r##"_foo* "##; let expected = r##"

_foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_366() { let original = r##"*foo bar * "##; let expected = r##"

*foo bar *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_367() { let original = r##"*foo bar * "##; let expected = r##"

*foo bar *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_368() { let original = r##"*(*foo) "##; let expected = r##"

*(*foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_369() { let original = r##"*(*foo*)* "##; let expected = r##"

(foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_370() { let original = r##"*foo*bar "##; let expected = r##"

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_371() { let original = r##"_foo bar _ "##; let expected = r##"

_foo bar _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_372() { let original = r##"_(_foo) "##; let expected = r##"

_(_foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_373() { let original = r##"_(_foo_)_ "##; let expected = r##"

(foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_374() { let original = r##"_foo_bar "##; let expected = r##"

_foo_bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_375() { let original = r##"_пристаням_стремятся "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_376() { let original = r##"_foo_bar_baz_ "##; let expected = r##"

foo_bar_baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_377() { let original = r##"_(bar)_. "##; let expected = r##"

(bar).

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_378() { let original = r##"**foo bar** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_379() { let original = r##"** foo bar** "##; let expected = r##"

** foo bar**

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_380() { let original = r##"a**"foo"** "##; let expected = r##"

a**"foo"**

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_381() { let original = r##"foo**bar** "##; let expected = r##"

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_382() { let original = r##"__foo bar__ "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_383() { let original = r##"__ foo bar__ "##; let expected = r##"

__ foo bar__

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_384() { let original = r##"__ foo bar__ "##; let expected = r##"

__ foo bar__

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_385() { let original = r##"a__"foo"__ "##; let expected = r##"

a__"foo"__

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_386() { let original = r##"foo__bar__ "##; let expected = r##"

foo__bar__

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_387() { let original = r##"5__6__78 "##; let expected = r##"

5__6__78

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_388() { let original = r##"пристаням__стремятся__ "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_389() { let original = r##"__foo, __bar__, baz__ "##; let expected = r##"

foo, bar, baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_390() { let original = r##"foo-__(bar)__ "##; let expected = r##"

foo-(bar)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_391() { let original = r##"**foo bar ** "##; let expected = r##"

**foo bar **

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_392() { let original = r##"**(**foo) "##; let expected = r##"

**(**foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_393() { let original = r##"*(**foo**)* "##; let expected = r##"

(foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_394() { let original = r##"**Gomphocarpus (*Gomphocarpus physocarpus*, syn. *Asclepias physocarpa*)** "##; let expected = r##"

Gomphocarpus (Gomphocarpus physocarpus, syn. Asclepias physocarpa)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_395() { let original = r##"**foo "*bar*" foo** "##; let expected = r##"

foo "bar" foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_396() { let original = r##"**foo**bar "##; let expected = r##"

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_397() { let original = r##"__foo bar __ "##; let expected = r##"

__foo bar __

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_398() { let original = r##"__(__foo) "##; let expected = r##"

__(__foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_399() { let original = r##"_(__foo__)_ "##; let expected = r##"

(foo)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_400() { let original = r##"__foo__bar "##; let expected = r##"

__foo__bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_401() { let original = r##"__пристаням__стремятся "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_402() { let original = r##"__foo__bar__baz__ "##; let expected = r##"

foo__bar__baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_403() { let original = r##"__(bar)__. "##; let expected = r##"

(bar).

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_404() { let original = r##"*foo [bar](/url)* "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_405() { let original = r##"*foo bar* "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_406() { let original = r##"_foo __bar__ baz_ "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_407() { let original = r##"_foo _bar_ baz_ "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_408() { let original = r##"__foo_ bar_ "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_409() { let original = r##"*foo *bar** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_410() { let original = r##"*foo **bar** baz* "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_411() { let original = r##"*foo**bar**baz* "##; let expected = r##"

foobarbaz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_412() { let original = r##"*foo**bar* "##; let expected = r##"

foo**bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_413() { let original = r##"***foo** bar* "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_414() { let original = r##"*foo **bar*** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_415() { let original = r##"*foo**bar*** "##; let expected = r##"

foobar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_416() { let original = r##"foo***bar***baz "##; let expected = r##"

foobarbaz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_417() { let original = r##"foo******bar*********baz "##; let expected = r##"

foobar***baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_418() { let original = r##"*foo **bar *baz* bim** bop* "##; let expected = r##"

foo bar baz bim bop

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_419() { let original = r##"*foo [*bar*](/url)* "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_420() { let original = r##"** is not an empty emphasis "##; let expected = r##"

** is not an empty emphasis

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_421() { let original = r##"**** is not an empty strong emphasis "##; let expected = r##"

**** is not an empty strong emphasis

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_422() { let original = r##"**foo [bar](/url)** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_423() { let original = r##"**foo bar** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_424() { let original = r##"__foo _bar_ baz__ "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_425() { let original = r##"__foo __bar__ baz__ "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_426() { let original = r##"____foo__ bar__ "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_427() { let original = r##"**foo **bar**** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_428() { let original = r##"**foo *bar* baz** "##; let expected = r##"

foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_429() { let original = r##"**foo*bar*baz** "##; let expected = r##"

foobarbaz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_430() { let original = r##"***foo* bar** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_431() { let original = r##"**foo *bar*** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_432() { let original = r##"**foo *bar **baz** bim* bop** "##; let expected = r##"

foo bar baz bim bop

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_433() { let original = r##"**foo [*bar*](/url)** "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_434() { let original = r##"__ is not an empty emphasis "##; let expected = r##"

__ is not an empty emphasis

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_435() { let original = r##"____ is not an empty strong emphasis "##; let expected = r##"

____ is not an empty strong emphasis

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_436() { let original = r##"foo *** "##; let expected = r##"

foo ***

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_437() { let original = r##"foo *\** "##; let expected = r##"

foo *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_438() { let original = r##"foo *_* "##; let expected = r##"

foo _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_439() { let original = r##"foo ***** "##; let expected = r##"

foo *****

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_440() { let original = r##"foo **\*** "##; let expected = r##"

foo *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_441() { let original = r##"foo **_** "##; let expected = r##"

foo _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_442() { let original = r##"**foo* "##; let expected = r##"

*foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_443() { let original = r##"*foo** "##; let expected = r##"

foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_444() { let original = r##"***foo** "##; let expected = r##"

*foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_445() { let original = r##"****foo* "##; let expected = r##"

***foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_446() { let original = r##"**foo*** "##; let expected = r##"

foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_447() { let original = r##"*foo**** "##; let expected = r##"

foo***

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_448() { let original = r##"foo ___ "##; let expected = r##"

foo ___

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_449() { let original = r##"foo _\__ "##; let expected = r##"

foo _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_450() { let original = r##"foo _*_ "##; let expected = r##"

foo *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_451() { let original = r##"foo _____ "##; let expected = r##"

foo _____

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_452() { let original = r##"foo __\___ "##; let expected = r##"

foo _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_453() { let original = r##"foo __*__ "##; let expected = r##"

foo *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_454() { let original = r##"__foo_ "##; let expected = r##"

_foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_455() { let original = r##"_foo__ "##; let expected = r##"

foo_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_456() { let original = r##"___foo__ "##; let expected = r##"

_foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_457() { let original = r##"____foo_ "##; let expected = r##"

___foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_458() { let original = r##"__foo___ "##; let expected = r##"

foo_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_459() { let original = r##"_foo____ "##; let expected = r##"

foo___

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_460() { let original = r##"**foo** "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_461() { let original = r##"*_foo_* "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_462() { let original = r##"__foo__ "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_463() { let original = r##"_*foo*_ "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_464() { let original = r##"****foo**** "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_465() { let original = r##"____foo____ "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_466() { let original = r##"******foo****** "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_467() { let original = r##"***foo*** "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_468() { let original = r##"_____foo_____ "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_469() { let original = r##"*foo _bar* baz_ "##; let expected = r##"

foo _bar baz_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_470() { let original = r##"*foo __bar *baz bim__ bam* "##; let expected = r##"

foo bar *baz bim bam

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_471() { let original = r##"**foo **bar baz** "##; let expected = r##"

**foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_472() { let original = r##"*foo *bar baz* "##; let expected = r##"

*foo bar baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_473() { let original = r##"*[bar*](/url) "##; let expected = r##"

*bar*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_474() { let original = r##"_foo [bar_](/url) "##; let expected = r##"

_foo bar_

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_475() { let original = r##"* "##; let expected = r##"

*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_476() { let original = r##"** "##; let expected = r##"

**

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_477() { let original = r##"__ "##; let expected = r##"

__

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_478() { let original = r##"*a `*`* "##; let expected = r##"

a *

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_479() { let original = r##"_a `_`_ "##; let expected = r##"

a _

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_480() { let original = r##"**a "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_481() { let original = r##"__a "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_482() { let original = r##"[link](/uri "title") "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_483() { let original = r##"[link](/uri) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_484() { let original = r##"[](./target.md) "##; let expected = r##"

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

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_486() { let original = r##"[link](<>) "##; let expected = r##"

link

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_488() { let original = r##"[link](/my uri) "##; let expected = r##"

[link](/my uri)

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

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_490() { let original = r##"[link](foo bar) "##; let expected = r##"

[link](foo bar)

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

[link]()

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_492() { let original = r##"[a]() "##; let expected = r##"

a

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

[link](<foo>)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_494() { let original = r##"[a]( [a](c) "##; let expected = r##"

[a](<b)c [a](<b)c> [a](c)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_495() { let original = r##"[link](\(foo\)) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_496() { let original = r##"[link](foo(and(bar))) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_497() { let original = r##"[link](foo(and(bar)) "##; let expected = r##"

[link](foo(and(bar))

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_498() { let original = r##"[link](foo\(and\(bar\)) "##; let expected = r##"

link

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

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_500() { let original = r##"[link](foo\)\:) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_501() { let original = r##"[link](#fragment) [link](https://example.com#fragment) [link](https://example.com?foo=3#frag) "##; let expected = r##"

link

link

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_502() { let original = r##"[link](foo\bar) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_503() { let original = r##"[link](foo%20bä) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_504() { let original = r##"[link]("title") "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_505() { let original = r##"[link](/url "title") [link](/url 'title') [link](/url (title)) "##; let expected = r##"

link link link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_506() { let original = r##"[link](/url "title \""") "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_507() { let original = r##"[link](/url "title") "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_508() { let original = r##"[link](/url "title "and" title") "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_509() { let original = r##"[link](/url 'title "and" title') "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_510() { let original = r##"[link]( /uri "title" ) "##; let expected = r##"

link

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_511() { let original = r##"[link] (/uri) "##; let expected = r##"

[link] (/uri)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_512() { let original = r##"[link [foo [bar]]](/uri) "##; let expected = r##"

link [foo [bar]]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_513() { let original = r##"[link] bar](/uri) "##; let expected = r##"

[link] bar](/uri)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_514() { let original = r##"[link [bar](/uri) "##; let expected = r##"

[link bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_515() { let original = r##"[link \[bar](/uri) "##; let expected = r##"

link [bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_516() { let original = r##"[link *foo **bar** `#`*](/uri) "##; let expected = r##"

link foo bar #

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_517() { let original = r##"[![moon](moon.jpg)](/uri) "##; let expected = r##"

moon

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_518() { let original = r##"[foo [bar](/uri)](/uri) "##; let expected = r##"

[foo bar](/uri)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_519() { let original = r##"[foo *[bar [baz](/uri)](/uri)*](/uri) "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_520() { let original = r##"![[[foo](uri1)](uri2)](uri3) "##; let expected = r##"

[foo](uri2)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_521() { let original = r##"*[foo*](/uri) "##; let expected = r##"

*foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_522() { let original = r##"[foo *bar](baz*) "##; let expected = r##"

foo *bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_523() { let original = r##"*foo [bar* baz] "##; let expected = r##"

foo [bar baz]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_524() { let original = r##"[foo "##; let expected = r##"

[foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_525() { let original = r##"[foo`](/uri)` "##; let expected = r##"

[foo](/uri)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_526() { let original = r##"[foo "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_527() { let original = r##"[foo][bar] [bar]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_528() { let original = r##"[link [foo [bar]]][ref] [ref]: /uri "##; let expected = r##"

link [foo [bar]]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_529() { let original = r##"[link \[bar][ref] [ref]: /uri "##; let expected = r##"

link [bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_530() { let original = r##"[link *foo **bar** `#`*][ref] [ref]: /uri "##; let expected = r##"

link foo bar #

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_531() { let original = r##"[![moon](moon.jpg)][ref] [ref]: /uri "##; let expected = r##"

moon

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_532() { let original = r##"[foo [bar](/uri)][ref] [ref]: /uri "##; let expected = r##"

[foo bar]ref

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_533() { let original = r##"[foo *bar [baz][ref]*][ref] [ref]: /uri "##; let expected = r##"

[foo bar baz]ref

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_534() { let original = r##"*[foo*][ref] [ref]: /uri "##; let expected = r##"

*foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_535() { let original = r##"[foo *bar][ref]* [ref]: /uri "##; let expected = r##"

foo *bar*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_536() { let original = r##"[foo [ref]: /uri "##; let expected = r##"

[foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_537() { let original = r##"[foo`][ref]` [ref]: /uri "##; let expected = r##"

[foo][ref]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_538() { let original = r##"[foo [ref]: /uri "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_539() { let original = r##"[foo][BaR] [bar]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_540() { let original = r##"[ẞ] [SS]: /url "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_541() { let original = r##"[Foo bar]: /url [Baz][Foo bar] "##; let expected = r##"

Baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_542() { let original = r##"[foo] [bar] [bar]: /url "title" "##; let expected = r##"

[foo] bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_543() { let original = r##"[foo] [bar] [bar]: /url "title" "##; let expected = r##"

[foo] bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_544() { let original = r##"[foo]: /url1 [foo]: /url2 [bar][foo] "##; let expected = r##"

bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_545() { let original = r##"[bar][foo\!] [foo!]: /url "##; let expected = r##"

[bar][foo!]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_546() { let original = r##"[foo][ref[] [ref[]: /uri "##; let expected = r##"

[foo][ref[]

[ref[]: /uri

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_547() { let original = r##"[foo][ref[bar]] [ref[bar]]: /uri "##; let expected = r##"

[foo][ref[bar]]

[ref[bar]]: /uri

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_548() { let original = r##"[[[foo]]] [[[foo]]]: /url "##; let expected = r##"

[[[foo]]]

[[[foo]]]: /url

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_549() { let original = r##"[foo][ref\[] [ref\[]: /uri "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_550() { let original = r##"[bar\\]: /uri [bar\\] "##; let expected = r##"

bar\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_551() { let original = r##"[] []: /uri "##; let expected = r##"

[]

[]: /uri

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_552() { let original = r##"[ ] [ ]: /uri "##; let expected = r##"

[ ]

[ ]: /uri

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_553() { let original = r##"[foo][] [foo]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_554() { let original = r##"[*foo* bar][] [*foo* bar]: /url "title" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_555() { let original = r##"[Foo][] [foo]: /url "title" "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_556() { let original = r##"[foo] [] [foo]: /url "title" "##; let expected = r##"

foo []

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_557() { let original = r##"[foo] [foo]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_558() { let original = r##"[*foo* bar] [*foo* bar]: /url "title" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_559() { let original = r##"[[*foo* bar]] [*foo* bar]: /url "title" "##; let expected = r##"

[foo bar]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_560() { let original = r##"[[bar [foo] [foo]: /url "##; let expected = r##"

[[bar foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_561() { let original = r##"[Foo] [foo]: /url "title" "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_562() { let original = r##"[foo] bar [foo]: /url "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_563() { let original = r##"\[foo] [foo]: /url "title" "##; let expected = r##"

[foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_564() { let original = r##"[foo*]: /url *[foo*] "##; let expected = r##"

*foo*

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_565() { let original = r##"[foo][bar] [foo]: /url1 [bar]: /url2 "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_566() { let original = r##"[foo][] [foo]: /url1 "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_567() { let original = r##"[foo]() [foo]: /url1 "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_568() { let original = r##"[foo](not a link) [foo]: /url1 "##; let expected = r##"

foo(not a link)

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_569() { let original = r##"[foo][bar][baz] [baz]: /url "##; let expected = r##"

[foo]bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_570() { let original = r##"[foo][bar][baz] [baz]: /url1 [bar]: /url2 "##; let expected = r##"

foobaz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_571() { let original = r##"[foo][bar][baz] [baz]: /url1 [foo]: /url2 "##; let expected = r##"

[foo]bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_572() { let original = r##"![foo](/url "title") "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_573() { let original = r##"![foo *bar*] [foo *bar*]: train.jpg "train & tracks" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_574() { let original = r##"![foo ![bar](/url)](/url2) "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_575() { let original = r##"![foo [bar](/url)](/url2) "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_576() { let original = r##"![foo *bar*][] [foo *bar*]: train.jpg "train & tracks" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_577() { let original = r##"![foo *bar*][foobar] [FOOBAR]: train.jpg "train & tracks" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_578() { let original = r##"![foo](train.jpg) "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_579() { let original = r##"My ![foo bar](/path/to/train.jpg "title" ) "##; let expected = r##"

My foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_580() { let original = r##"![foo]() "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_581() { let original = r##"![](/url) "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_582() { let original = r##"![foo][bar] [bar]: /url "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_583() { let original = r##"![foo][bar] [BAR]: /url "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_584() { let original = r##"![foo][] [foo]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_585() { let original = r##"![*foo* bar][] [*foo* bar]: /url "title" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_586() { let original = r##"![Foo][] [foo]: /url "title" "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_587() { let original = r##"![foo] [] [foo]: /url "title" "##; let expected = r##"

foo []

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_588() { let original = r##"![foo] [foo]: /url "title" "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_589() { let original = r##"![*foo* bar] [*foo* bar]: /url "title" "##; let expected = r##"

foo bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_590() { let original = r##"![[foo]] [[foo]]: /url "title" "##; let expected = r##"

![[foo]]

[[foo]]: /url "title"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_591() { let original = r##"![Foo] [foo]: /url "title" "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_592() { let original = r##"!\[foo] [foo]: /url "title" "##; let expected = r##"

![foo]

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_593() { let original = r##"\![foo] [foo]: /url "title" "##; let expected = r##"

!foo

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

http://foo.bar.baz

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

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

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

irc://foo.bar:2233/baz

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

MAILTO:FOO@BAR.BAZ

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

a+b+c:d

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

made-up-scheme://foo,bar

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

https://../

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

localhost:5001/foo

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

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

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

https://example.com/\[\

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

foo@bar.example.com

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

foo+special@Bar.baz-bar0.com

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

<foo+@bar.example.com>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_607() { let original = r##"<> "##; let expected = r##"

<>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_608() { let original = r##"< https://foo.bar > "##; let expected = r##"

< https://foo.bar >

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

<m:abc>

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

<foo.bar.baz>

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

https://example.com

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_612() { let original = r##"foo@bar.example.com "##; let expected = r##"

foo@bar.example.com

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

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_615() { let original = r##" "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_616() { let original = r##" "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_617() { let original = r##"Foo "##; let expected = r##"

Foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_618() { let original = r##"<33> <__> "##; let expected = r##"

<33> <__>

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

<a h*#ref="hi">

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_620() { let original = r##"
<a href="hi'> <a href=hi'>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_621() { let original = r##"< a>< foo> "##; let expected = r##"

< a>< foo><bar/ > <foo bar=baz bim!bop />

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

<a href='bar'title=title>

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

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

</a href="foo">

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_625() { let original = r##"foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_626() { let original = r##"foo foo --> foo foo --> "##; let expected = r##"

foo foo -->

foo foo -->

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_627() { let original = r##"foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_628() { let original = r##"foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_629() { let original = r##"foo &<]]> "##; let expected = r##"

foo &<]]>

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_630() { let original = r##"foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_631() { let original = r##"foo "##; let expected = r##"

foo

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

<a href=""">

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_633() { let original = r##"foo baz "##; let expected = r##"

foo
baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_634() { let original = r##"foo\ baz "##; let expected = r##"

foo
baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_635() { let original = r##"foo baz "##; let expected = r##"

foo
baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_636() { let original = r##"foo bar "##; let expected = r##"

foo
bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_637() { let original = r##"foo\ bar "##; let expected = r##"

foo
bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_638() { let original = r##"*foo bar* "##; let expected = r##"

foo
bar

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_639() { let original = r##"*foo\ bar* "##; let expected = r##"

foo
bar

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

code span

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_641() { let original = r##"`code\ span` "##; let expected = r##"

code\ span

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_642() { let original = r##"
"##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_643() { let original = r##" "##; let expected = r##"

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_644() { let original = r##"foo\ "##; let expected = r##"

foo\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_645() { let original = r##"foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_646() { let original = r##"### foo\ "##; let expected = r##"

foo\

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_647() { let original = r##"### foo "##; let expected = r##"

foo

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_648() { let original = r##"foo baz "##; let expected = r##"

foo baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_649() { let original = r##"foo baz "##; let expected = r##"

foo baz

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_650() { let original = r##"hello $.;'there "##; let expected = r##"

hello $.;'there

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_651() { let original = r##"Foo χρῆν "##; let expected = r##"

Foo χρῆν

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn spec_test_652() { let original = r##"Multiple spaces "##; let expected = r##"

Multiple spaces

"##; test_markdown_html(original, expected, false, false, false); } pulldown-cmark-0.10.3/tests/suite/strikethrough.rs000064400000000000000000000073731046102023000204210ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn strikethrough_test_1() { let original = r##"~~This is *stricken out*~~ "##; let expected = r##"

This is stricken out

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

This is ~~stricken

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

Thisisstricken

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

Thisisstricken

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_5() { let original = r##"Here I strike out an exclamation point~~!~~. "##; let expected = r##"

Here I strike out an exclamation point!.

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

This is stricken out

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

This is ~stricken

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

This~is~nothing

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

This~is~nothing

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_10() { let original = r##"Here I fail to strike out an exclamation point~!~. "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_11() { let original = r##"Here I fail to strike out a tilde ~~~. "##; let expected = r##"

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

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_12() { let original = r##"Here I fail to match up ~~tildes~. "##; let expected = r##"

Here I fail to match up ~~tildes~.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_13() { let original = r##"Here I fail to match up ~tildes~~. "##; let expected = r##"

Here I fail to match up ~tildes~~.

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

This ~is stricken.

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn strikethrough_test_15() { let original = r##"~This ~~is stricken.~ "##; 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); } pulldown-cmark-0.10.3/tests/suite/table.rs000064400000000000000000000347321046102023000166050ustar 00000000000000// This file is auto-generated by the build script // Please, do not modify it manually use super::test_markdown_html; #[test] fn table_test_1() { let original = r##"Test header ----------- "##; let expected = r##"

Test header

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_2() { let original = r##"Test|Table ----|----- "##; let expected = r##"
TestTable
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_3() { let original = r##"> Test | Table > ------|------ > Row 1 | Every > Row 2 | Day > > Paragraph "##; let expected = r##"
TestTable
Row 1Every
Row 2Day

Paragraph

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_4() { let original = r##" 1. First entry 2. Second entry Col 1|Col 2 -|- Row 1|Part 2 Row 2|Part 2 "##; let expected = r##"
  1. First entry

  2. Second entry

    Col 1Col 2
    Row 1Part 2
    Row 2Part 2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_5() { let original = r##"|Col 1|Col 2| |-----|-----| |R1C1 |R1C2 | |R2C1 |R2C2 | "##; let expected = r##"
Col 1Col 2
R1C1R1C2
R2C1R2C2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_6() { let original = r##"| Col 1 | Col 2 | |-------|-------| | | | | | | "##; let expected = r##"
Col 1Col 2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_7() { let original = r##"| Col 1 | Col 2 | |-------|-------| | x | | | | x | "##; let expected = r##"
Col 1Col 2
x
x
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_8() { let original = r##"|Col 1|Col 2| |-----|-----| |✓ |✓ | |✓ |✓ | "##; let expected = r##"
Col 1Col 2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_9() { let original = r##"| Target | std |rustc|cargo| notes | |-------------------------------|-----|-----|-----|----------------------------| | `x86_64-unknown-linux-musl` | ✓ | | | 64-bit Linux with MUSL | | `arm-linux-androideabi` | ✓ | | | ARM Android | | `arm-unknown-linux-gnueabi` | ✓ | ✓ | | ARM Linux (2.6.18+) | | `arm-unknown-linux-gnueabihf` | ✓ | ✓ | | ARM Linux (2.6.18+) | | `aarch64-unknown-linux-gnu` | ✓ | | | ARM64 Linux (2.6.18+) | | `mips-unknown-linux-gnu` | ✓ | | | MIPS Linux (2.6.18+) | | `mipsel-unknown-linux-gnu` | ✓ | | | MIPS (LE) Linux (2.6.18+) | "##; let expected = r##"
Targetstdrustccargonotes
x86_64-unknown-linux-musl64-bit Linux with MUSL
arm-linux-androideabiARM Android
arm-unknown-linux-gnueabiARM Linux (2.6.18+)
arm-unknown-linux-gnueabihfARM Linux (2.6.18+)
aarch64-unknown-linux-gnuARM64 Linux (2.6.18+)
mips-unknown-linux-gnuMIPS Linux (2.6.18+)
mipsel-unknown-linux-gnuMIPS (LE) Linux (2.6.18+)
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_10() { let original = r##"|-|-| |ぃ|い| "##; let expected = r##"

|-|-| |ぃ|い|

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_11() { let original = r##"|ぁ|ぃ| |-|-| |ぃ|ぃ| "##; let expected = r##"
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_12() { let original = r##"|Колонка 1|Колонка 2| |---------|---------| |Ячейка 1 |Ячейка 2 | "##; let expected = r##"
Колонка 1Колонка 2
Ячейка 1Ячейка 2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_13() { let original = r##"table a | a | b | | --- | --- | | c | d | table b | a | b | | --- | --- | | c | d | table c a | b --- | --- c | d table d a | b --|-- c | d table e a | b --|-- c | d table f | a | b | | --- | --- | | c | d | table g a | b --- | --- c | d table h a |-| b table i | a |- b table j | a - b "##; let expected = r##"

table a

ab
cd

table b | a | b | | --- | --- | | c | d |

table c a | b --- | --- c | d

table d a | b --|-- c | d

table e a | b --|-- c | d

table f

ab
cd

table g a | b --- | --- c | d

table h a |-| b

table i

a
b

table j | a

b

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_14() { let original = r##"a | b - | - 1 | 2 "##; let expected = r##"
ab
12
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_15() { let original = r##"a | b\ - | - 1 | 2 "##; let expected = r##"

a | b\

  • | - 1 | 2
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_16() { let original = r##"a\ | b | c | |---|---| | d | e | "##; let expected = r##"

a\

bc
de
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_17() { let original = r##"| Description | Test case | |-------------|-----------| | Single | `\` | | Double | `\\` | | Basic test | `\|` | | Basic test 2| `\|\|\` | | Basic test 3| `x\|y\|z\`| | Not pipe | `\.` | | Combo | `\.\|\` | | Extra | `\\\.` | | Wait, what? | `\\|` | | Wait, what? | `\\\|` | | Wait, what? | `\\\\|` | | Wait, what? | `\\\\\|` | | Wait, what? | \| | Wait, what? | \\| | Wait, what? | \\\| | Wait, what?x| \|x | Wait, what?x| \\|x | Wait, what?x| \\\|x | Direct trail| \.|x "##; let expected = r##"
DescriptionTest case
Single\
Double\\
Basic test|
Basic test 2||\
Basic test 3x|y|z\
Not pipe\.
Combo\.|\
Extra\\\.
Wait, what?\|
Wait, what?\\|
Wait, what?\\\|
Wait, what?\\\\|
Wait, what?|
Wait, what?|
Wait, what?\|
Wait, what?x|x
Wait, what?x|x
Wait, what?x\|x
Direct trail.
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_18() { let original = r##"| Single | `\|` | |--|--| | Single | `\|` | | Double | `\\|` | |--|--| | Double | `\\|` | | Double Twice | `\\|\\|` | |--|--| | Double Twice | `\\|\\|` | | Triple | `\\\|` | |--|--| | Triple | `\\\|` | "##; let expected = r##"
Single|
Single|
Double\|
Double\|
Double Twice\|\|
Double Twice\|\|
Triple\\|
Triple\\|
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_19() { let original = r##"| Table | Header | |-------|--------| | Table | Body | | | Not | Enough | | Table | Header | |-------|--------| | Table | Body | | | Not | Enough | "##; let expected = r##"
TableHeader
TableBody

| | Not | Enough |

TableHeader
TableBody

| | Not | Enough |

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_20() { let original = r##"| Table | Header | |-------|--------| | "##; let expected = r##"
TableHeader

|

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_21() { let original = r##"| |-------|--------| | Table | Body | "##; let expected = r##"

| |-------|--------| | Table | Body |

"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_22() { let original = r##"| Single | [test](first\|second) | |--|--| | Double | [test](first\\|second) | |--|--| | Triple | [test](first\\\|second) | |--|--| "##; let expected = r##"
Singletest
Doubletest
Tripletest
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_23() { let original = r##"| Single | [first\|second] | |--|--| | Double | [first\\|second] | |--|--| | Triple | [first\\\|second] | |--|--| [first\|second]: https://rust-lang.org [first\\|second]: https://docs.rs "##; let expected = r##"
Single[first|second]
Doublefirst|second
Triplefirst\|second
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_24() { let original = r##"Q: Knock knock. A: Who's there. Q: Interrupting cow. A: Interrupting —? | `Moo\\|ooo` | |-------------| | `ooo\\|ooo` | "##; let expected = r##"

Q: Knock knock. A: Who's there. Q: Interrupting cow. A: Interrupting —?

Moo\|ooo
ooo\|ooo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_25() { let original = r##"| ![Moo\\|Moo](image.png) | |-------------| | ![Moo\\\|Moo](image.png) | "##; let expected = r##"
Moo|Moo
Moo\|Moo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_26() { let original = r##"| [Moo](https://example.org "Example\\|Link") | |---------------------------------------------| | [Moo](https://example.org "Example\\\|Link") | "##; let expected = r##"
Moo
Moo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_27() { let original = r##"moo | moo ----|---- moo | moo * "##; let expected = r##"
moomoo
moomoo
"##; test_markdown_html(original, expected, false, false, false); } #[test] fn table_test_28() { let original = r##"moo | moo ----|---- moo | moo 2. "##; let expected = r##"
moomoo
moomoo
"##; test_markdown_html(original, expected, false, false, false); }