diff -Nru rust-comrak-0.18.0/.cargo_vcs_info.json rust-comrak-0.20.0/.cargo_vcs_info.json --- rust-comrak-0.18.0/.cargo_vcs_info.json 1970-01-01 00:00:01.000000000 +0000 +++ rust-comrak-0.20.0/.cargo_vcs_info.json 1970-01-01 00:00:01.000000000 +0000 @@ -1,6 +1,6 @@ { "git": { - "sha1": "8fc0bee486370f5a8512382745bb29b3ba713e97" + "sha1": "4c909b654cfd3600ab3e96c7186228ba7c8f79cd" }, "path_in_vcs": "" } \ No newline at end of file diff -Nru rust-comrak-0.18.0/.github/workflows/benchmarks.yml rust-comrak-0.20.0/.github/workflows/benchmarks.yml --- rust-comrak-0.18.0/.github/workflows/benchmarks.yml 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/.github/workflows/benchmarks.yml 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,36 @@ +name: benchmarks + +on: + pull_request: + types: + - opened + - reopened + issue_comment: + types: + - created + +jobs: + run_benchmarks: + runs-on: ubuntu-latest + permissions: + pull-requests: write + # run either when pull request is opened or when comment body (only on pr) is /run-bench + if: (github.event_name == 'pull_request') || ((github.event.issue.pull_request != null) && github.event.comment.body == '/run-bench') + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install hyperfine + run: cargo install hyperfine + - name: Install cmake + run: sudo apt-get update && sudo apt-get install cmake -y + - name: Build Binaries + run: make binaries + - name: Run Benchmarks + run: make bench-all + - name: Post result comment + uses: mshick/add-pr-comment@v2 + with: + message-path: bench-output.md \ No newline at end of file diff -Nru rust-comrak-0.18.0/.github/workflows/rust.yml rust-comrak-0.20.0/.github/workflows/rust.yml --- rust-comrak-0.18.0/.github/workflows/rust.yml 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/.github/workflows/rust.yml 2006-07-24 01:21:28.000000000 +0000 @@ -79,17 +79,16 @@ run: cargo +stable build --locked --release --all-features clippy_format: runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 with: submodules: true - - name: Obtain Rust - run: rustup override set ${{ matrix.rust }} + - uses: actions/checkout@v3 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v4 + - name: Run the Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@v2 - name: Check clippy - run: rustup component add clippy && cargo clippy + run: nix build .#checks.x86_64-linux.comrak-clippy -L - name: Check formatting - run: rustup component add rustfmt && cargo fmt -- --check + run: nix build .#checks.x86_64-linux.comrak-fmt -L diff -Nru rust-comrak-0.18.0/.gitignore rust-comrak-0.20.0/.gitignore --- rust-comrak-0.18.0/.gitignore 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/.gitignore 2006-07-24 01:21:28.000000000 +0000 @@ -1,3 +1,11 @@ target comrak-* .vscode +.idea +vendor/comrak +vendor/progit +benches/cmark-gfm +benches/comrak-* +benches/pulldown-cmark +benches/markdown-it +/result* diff -Nru rust-comrak-0.18.0/.gitmodules rust-comrak-0.20.0/.gitmodules --- rust-comrak-0.18.0/.gitmodules 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/.gitmodules 2006-07-24 01:21:28.000000000 +0000 @@ -1,3 +1,9 @@ [submodule "vendor/cmark-gfm"] path = vendor/cmark-gfm - url = https://github.com/kivikakk/cmark-gfm.git + url = https://github.com/github/cmark-gfm.git +[submodule "vendor/pulldown-cmark"] + path = vendor/pulldown-cmark + url = https://github.com/raphlinus/pulldown-cmark.git +[submodule "vendor/markdown-it"] + path = vendor/markdown-it + url = https://github.com/rlidwka/markdown-it.rs.git diff -Nru rust-comrak-0.18.0/Cargo.lock rust-comrak-0.20.0/Cargo.lock --- rust-comrak-0.18.0/Cargo.lock 1970-01-01 00:00:01.000000000 +0000 +++ rust-comrak-0.20.0/Cargo.lock 1970-01-01 00:00:01.000000000 +0000 @@ -126,10 +126,11 @@ [[package]] name = "comrak" -version = "0.18.0" +version = "0.20.0" dependencies = [ "arbitrary", "clap", + "derive_builder", "emojis", "entities", "memchr", @@ -155,6 +156,41 @@ ] [[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] name = "derive_arbitrary" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -166,6 +202,37 @@ ] [[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] name = "deunicode" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -306,6 +373,12 @@ ] [[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] name = "indexmap" version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -557,9 +630,9 @@ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] @@ -1087,9 +1160,9 @@ [[package]] name = "xml-rs" -version = "0.8.4" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" [[package]] name = "yaml-rust" diff -Nru rust-comrak-0.18.0/Cargo.toml rust-comrak-0.20.0/Cargo.toml --- rust-comrak-0.18.0/Cargo.toml 1970-01-01 00:00:01.000000000 +0000 +++ rust-comrak-0.20.0/Cargo.toml 1970-01-01 00:00:01.000000000 +0000 @@ -12,7 +12,7 @@ [package] edition = "2018" name = "comrak" -version = "0.18.0" +version = "0.20.0" authors = ["Asherah Connor "] exclude = [ "/hooks/*", @@ -39,6 +39,13 @@ repository = "https://github.com/kivikakk/comrak" resolver = "2" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + [profile.release] lto = true @@ -55,6 +62,9 @@ features = ["derive"] optional = true +[dependencies.derive_builder] +version = "0.12.0" + [dependencies.emojis] version = "0.5.2" optional = true diff -Nru rust-comrak-0.18.0/Cargo.toml.orig rust-comrak-0.20.0/Cargo.toml.orig --- rust-comrak-0.18.0/Cargo.toml.orig 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/Cargo.toml.orig 2006-07-24 01:21:28.000000000 +0000 @@ -1,6 +1,6 @@ [package] name = "comrak" -version = "0.18.0" +version = "0.20.0" authors = ["Asherah Connor "] description = "A 100% CommonMark-compatible GitHub Flavored Markdown parser and formatter" documentation = "https://docs.rs/comrak" @@ -21,6 +21,10 @@ resolver = "2" edition = "2018" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [profile.release] lto = true @@ -40,6 +44,7 @@ slug = "0.1.4" emojis = { version = "0.5.2", optional = true } arbitrary = { version = "1", optional = true, features = ["derive"] } +derive_builder = "0.12.0" [dev-dependencies] ntest = "0.9" diff -Nru rust-comrak-0.18.0/README.md rust-comrak-0.20.0/README.md --- rust-comrak-0.18.0/README.md 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/README.md 2006-07-24 01:21:28.000000000 +0000 @@ -6,7 +6,7 @@ [![crates.io version](https://img.shields.io/crates/v/comrak.svg)](https://crates.io/crates/comrak) [![docs.rs](https://docs.rs/comrak/badge.svg)](https://docs.rs/comrak) -Rust port of [github's `cmark-gfm`](https://github.com/github/cmark). +Rust port of [github's `cmark-gfm`](https://github.com/github/cmark). *Currently synced with release `0.29.0.gfm.13`*. - [Installation](#installation) - [Usage](#usage) @@ -22,7 +22,7 @@ ``` toml [dependencies] -comrak = "0.18" +comrak = "0.20" ``` Comrak supports Rust stable. @@ -76,6 +76,9 @@ --relaxed-tasklist-character Enable relaxing which character is allowed in a tasklists + --relaxed-autolinks + Enable relaxing of autolink parsing, allowing links to be recognized when in brackets + --default-info-string Default value for fenced code block's info strings if none is given @@ -144,8 +147,8 @@ And there's a Rust interface. You can use `comrak::markdown_to_html` directly: ``` rust -use comrak::{markdown_to_html, ComrakOptions}; -assert_eq!(markdown_to_html("Hello, **世界**!", &ComrakOptions::default()), +use comrak::{markdown_to_html, Options}; +assert_eq!(markdown_to_html("Hello, **世界**!", &Options::default()), "

Hello, 世界!

\n"); ``` @@ -153,7 +156,7 @@ ``` rust extern crate comrak; -use comrak::{parse_document, format_html, Arena, ComrakOptions}; +use comrak::{parse_document, format_html, Arena, Options}; use comrak::nodes::{AstNode, NodeValue}; // The returned nodes are created in the supplied Arena, and are bound by its lifetime. @@ -162,7 +165,7 @@ let root = parse_document( &arena, "This is my input.\n\n1. Also my input.\n2. Certainly my input.\n", - &ComrakOptions::default()); + &Options::default()); fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) where F : Fn(&'a AstNode<'a>) { @@ -183,7 +186,7 @@ }); let mut html = vec![]; -format_html(root, &ComrakOptions::default(), &mut html).unwrap(); +format_html(root, &Options::default(), &mut html).unwrap(); assert_eq!( String::from_utf8(html).unwrap(), @@ -194,6 +197,34 @@ \n"); ``` +## Benchmarking + +For running benchmarks, you will need to [install hyperfine](https://github.com/sharkdp/hyperfine#installation) and optionally cmake. + +If you want to just run the benchmark for `comrak`, with the current state of the repo, you can simply run + +``` bash +make bench-comrak +``` + +This will build comrak in release mode, and run benchmark on it. You will see the time measurements as reported by hyperfine in the console. + +Makefile also provides a way to run benchmarks for `comrak` current state (with your changes), `comrak` main branch, [`cmark-gfm`](https://github.com/github/cmark-gfm), [`pulldown-cmark`](https://github.com/raphlinus/pulldown-cmark) and [`markdown-it.rs`](https://github.com/rlidwka/markdown-it.rs). For this you will need to install `cmake`. After that make sure that you have set-up the git submodules. In case you have not installed submodules when cloning, you can do it by running + +``` bash +git submodule update --init +``` + +After this is done, you can run + +``` bash +make bench-all +``` + +which will run benchmarks across all, and report the time take by each as well as relative time. + +Apart from this, CI is also setup for running benchmarks when a pull request is first opened. It will add a comment with the results on the pull request in a tabular format comparing the 5 versions. After that you can manually trigger this CI by commenting `/run-bench` on the PR, this will update the existing comment with new results. Note benchmarks won't be automatically run on each push. + ## Security As with [`cmark`](https://github.com/commonmark/cmark) and [`cmark-gfm`](https://github.com/github/cmark-gfm#security), @@ -224,7 +255,7 @@ - Shortcodes By default none are enabled; they are individually enabled with each parse by setting the appropriate values in the -[`ComrakOptions` struct](https://docs.rs/comrak/newest/comrak/struct.ComrakOptions.html). +[`ComrakExtensionOptions` struct](https://docs.rs/comrak/newest/comrak/struct.ComrakExtensionOptions.html). ## Plugins @@ -233,7 +264,7 @@ At the moment syntax highlighting of codefence blocks is the only feature that can be enhanced with plugins. Create an implementation of the `SyntaxHighlighterAdapter` trait, and then provide an instance of such adapter to -`ComrakPlugins.render.codefence_syntax_highlighter`. For formatting a markdown document with plugins, use the +`Plugins.render.codefence_syntax_highlighter`. For formatting a markdown document with plugins, use the `markdown_to_html_with_plugins` function, which accepts your plugin as a parameter. See the `syntax_highlighter.rs` and `syntect.rs` examples for more details. @@ -242,7 +273,7 @@ [`syntect`](https://github.com/trishume/syntect) is a syntax highlighting library for Rust. By default, `comrak` offers a plugin for it. In order to utilize it, create an instance of `plugins::syntect::SyntectAdapter` and use it as your -`ComrakPlugins` option. +`Plugins` option. ## Related projects diff -Nru rust-comrak-0.18.0/RELEASE_CHECKLIST.md rust-comrak-0.20.0/RELEASE_CHECKLIST.md --- rust-comrak-0.18.0/RELEASE_CHECKLIST.md 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/RELEASE_CHECKLIST.md 2006-07-24 01:21:28.000000000 +0000 @@ -1,16 +1,16 @@ -* [ ] `rustup update stable` -* [ ] bump version in Cargo.toml - * [ ] did `tests::exercise_full_api` change? if so, it's a semver-breaking change. -* [ ] update changelog -* [ ] `cargo run --example update-readme` -* [ ] commit, tag, push commit, but do not push tag yet -* build binaries: - * [ ] `script/release-skye` - * [ ] `script/release-skye-debian` in Parallels - * [ ] `script\release-raven.ps1` ("Windows PowerShell" works, make sure to run with comrak root as cwd) - * [ ] `script/release-raven-freebsd` in VMWare - * [ ] `script/release-sarah` -* [ ] `script/assemble-releases` -* [ ] `cargo publish` -* [ ] push tag -* [ ] edit release to include changelog +- [ ] `rustup update stable` +- [ ] bump version in Cargo.toml + - [ ] did `tests::exercise_full_api` change? if so, it's a semver-breaking change. +- [ ] update changelog +- [ ] `cargo run --example update-readme` +- [ ] commit, tag, push commit, but do not push tag yet +- build binaries: + - [ ] `script/release-skye` + - [ ] `script/release-skye-debian` in Parallels + - [ ] `script\release-raven.ps1` ("Windows PowerShell" works, make sure to run with comrak root as cwd) + - [ ] `script/release-sayya` in VMWare + - [ ] `script/release-bunjil-wsl` +- [ ] Check you have 7 binaries: aarch64-apple-darwin, aarch64-unknown-linux-gnu, armv7-unknown-linux-musleabihf, x86\_64-apple-darwin, x86\_64-pc-windows-msvc, x86\_64-unknown-freebsd, x86\_64-unknown-linux-gnu +- [ ] `cargo publish` +- [ ] push tag +- [ ] edit release to include changelog diff -Nru rust-comrak-0.18.0/benches/bench.sh rust-comrak-0.20.0/benches/bench.sh --- rust-comrak-0.18.0/benches/bench.sh 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/benches/bench.sh 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,8 @@ +#! /bin/bash + +PROG=$1 +ROOTDIR=$(git rev-parse --show-toplevel) + +for lang in ar az be ca cs de en eo es es-ni fa fi fr hi hu id it ja ko mk nl no-nb pl pt-br ro ru sr th tr uk vi zh zh-tw; do \ + cat $ROOTDIR/vendor/progit/$lang/*/*.markdown | $PROG > /dev/null +done \ No newline at end of file diff -Nru rust-comrak-0.18.0/benches/progit.rs rust-comrak-0.20.0/benches/progit.rs --- rust-comrak-0.18.0/benches/progit.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/benches/progit.rs 2006-07-24 01:21:28.000000000 +0000 @@ -2,7 +2,7 @@ extern crate test; -use comrak::{format_html, parse_document, Arena, ComrakOptions}; +use comrak::{format_html, parse_document, Arena, Options}; use test::Bencher; #[bench] @@ -15,8 +15,8 @@ file.read_to_string(&mut s).unwrap(); b.iter(|| { let arena = Arena::new(); - let root = parse_document(&arena, &s, &ComrakOptions::default()); + let root = parse_document(&arena, &s, &Options::default()); let mut output = vec![]; - format_html(root, &ComrakOptions::default(), &mut output).unwrap() + format_html(root, &Options::default(), &mut output).unwrap() }); } diff -Nru rust-comrak-0.18.0/changelog.txt rust-comrak-0.20.0/changelog.txt --- rust-comrak-0.18.0/changelog.txt 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/changelog.txt 2006-07-24 01:21:28.000000000 +0000 @@ -1,3 +1,37 @@ +### 0.20.0 + +* build(deps): bump rustix from 0.36.11 to 0.36.16 in /fuzz by @dependabot in https://github.com/kivikakk/comrak/pull/346 +* Use Nix for CI. by @charlottia in https://github.com/kivikakk/comrak/pull/338 +* Allow for Syntect to simply generate CSS classes by @gjtorikian in https://github.com/kivikakk/comrak/pull/347 + + +### 0.19.0 + +* Simplify anchorize() by @kornelski in https://github.com/kivikakk/comrak/pull/297 +* Use footnote name for reference id by @digitalmoksha in https://github.com/kivikakk/comrak/pull/300 +* Escape footnote name by @digitalmoksha in https://github.com/kivikakk/comrak/pull/308 +* Add in-doc labels for public facing features by @CosmicHorrorDev in https://github.com/kivikakk/comrak/pull/304 +* build(deps): bump xml-rs from 0.8.4 to 0.8.14 by @dependabot in https://github.com/kivikakk/comrak/pull/312 +* Handle footnote names that have been parsed into multiple nodes by @digitalmoksha in https://github.com/kivikakk/comrak/pull/311 +* Sync with cmark-gfm-0.29.0.gfm.3 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/313 +* Sync with cmark-gfm-0.29.0.gfm.4 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/314 +* Sync with cmark-gfm-0.29.0.gfm.5 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/315 +* Fix backslash in a link issue by @vpetrigo in https://github.com/kivikakk/comrak/pull/317 +* Sync with cmark-gfm-0.29.0.gfm.7 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/318 +* Rename `ComrakFoo` types to just `Foo` for easier usage by @tgross35 in https://github.com/kivikakk/comrak/pull/320 +* Make `ComrakExtensionOptions` non-exhaustive by @CosmicHorrorDev in https://github.com/kivikakk/comrak/pull/305 +* Add builder derive and non_exhaustive for option structs by @YJDoc2 in https://github.com/kivikakk/comrak/pull/292 +* add PartialEq and Eq derive for Ast and its components by @YJDoc2 in https://github.com/kivikakk/comrak/pull/322 +* Sync with cmark-gfm-0.29.0.gfm.11 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/319 +* Fix autolink detection inside wiki style link brackets by @digitalmoksha in https://github.com/kivikakk/comrak/pull/325 +* Add CI for running benchmarks by @YJDoc2 in https://github.com/kivikakk/comrak/pull/326 +* Make adapters Send + Sync by @lucperkins in https://github.com/kivikakk/comrak/pull/337 +* docs: fix-up broken docs.rs link by @silverjam in https://github.com/kivikakk/comrak/pull/341 +* Use github/cmark-gfm submodule by @digitalmoksha in https://github.com/kivikakk/comrak/pull/344 +* Sync with cmark-gfm-0.29.0.gfm.12 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/343 +* Sync with cmark-gfm-0.29.0.gfm.13 by @digitalmoksha in https://github.com/kivikakk/comrak/pull/345 + + ### 0.18.0 * Improve performance of bundled plugins, and streaming I/O by @kivikakk in https://github.com/kivikakk/comrak/pull/288 diff -Nru rust-comrak-0.18.0/debian/changelog rust-comrak-0.20.0/debian/changelog --- rust-comrak-0.18.0/debian/changelog 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/changelog 2024-01-05 08:57:59.000000000 +0000 @@ -1,3 +1,18 @@ +rust-comrak (0.20.0-2) unstable; urgency=medium + + * Team upload. + * Package comrak 0.20.0 from crates.io using debcargo 2.6.1 + * Relax the emojis dep + + -- Sylvestre Ledru Fri, 05 Jan 2024 09:57:59 +0100 + +rust-comrak (0.20.0-1) unstable; urgency=medium + + * Team upload. + * Package comrak 0.20.0 from crates.io using debcargo 2.6.1 + + -- Sylvestre Ledru Fri, 05 Jan 2024 09:27:01 +0100 + rust-comrak (0.18.0-1) unstable; urgency=medium * Team upload. diff -Nru rust-comrak-0.18.0/debian/control rust-comrak-0.20.0/debian/control --- rust-comrak-0.18.0/debian/control 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/control 2024-01-05 08:57:59.000000000 +0000 @@ -10,6 +10,7 @@ librust-clap-4+derive-dev , librust-clap-4+string-dev , librust-clap-4+wrap-help-dev , + librust-derive-builder-0.12+default-dev , librust-entities-1+default-dev (>= 1.0.1-~~) , librust-memchr-2+default-dev , librust-once-cell-1+default-dev (>= 1.13.0-~~) , @@ -27,7 +28,7 @@ Maintainer: Debian Rust Maintainers Uploaders: Arnaud Ferraris -Standards-Version: 4.6.1 +Standards-Version: 4.6.2 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/comrak] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/comrak Homepage: https://github.com/kivikakk/comrak @@ -45,7 +46,8 @@ librust-clap-4+derive-dev, librust-clap-4+string-dev, librust-clap-4+wrap-help-dev, - librust-emojis-0.5+default-dev (>= 0.5.2-~~), + librust-derive-builder-0.12+default-dev, + librust-emojis-0+default-dev, librust-entities-1+default-dev (>= 1.0.1-~~), librust-memchr-2+default-dev, librust-once-cell-1+default-dev (>= 1.13.0-~~), @@ -80,26 +82,25 @@ librust-comrak-0+shortcodes-dev (= ${binary:Version}), librust-comrak-0+syntect-dev (= ${binary:Version}), librust-comrak-0+xdg-dev (= ${binary:Version}), - librust-comrak-0.18-dev (= ${binary:Version}), - librust-comrak-0.18+arbitrary-dev (= ${binary:Version}), - librust-comrak-0.18+clap-dev (= ${binary:Version}), - librust-comrak-0.18+cli-dev (= ${binary:Version}), - librust-comrak-0.18+default-dev (= ${binary:Version}), - librust-comrak-0.18+emojis-dev (= ${binary:Version}), - librust-comrak-0.18+shell-words-dev (= ${binary:Version}), - librust-comrak-0.18+shortcodes-dev (= ${binary:Version}), - librust-comrak-0.18+syntect-dev (= ${binary:Version}), - librust-comrak-0.18+xdg-dev (= ${binary:Version}), - librust-comrak-0.18.0-dev (= ${binary:Version}), - librust-comrak-0.18.0+arbitrary-dev (= ${binary:Version}), - librust-comrak-0.18.0+clap-dev (= ${binary:Version}), - librust-comrak-0.18.0+cli-dev (= ${binary:Version}), - librust-comrak-0.18.0+default-dev (= ${binary:Version}), - librust-comrak-0.18.0+emojis-dev (= ${binary:Version}), - librust-comrak-0.18.0+shell-words-dev (= ${binary:Version}), - librust-comrak-0.18.0+shortcodes-dev (= ${binary:Version}), - librust-comrak-0.18.0+syntect-dev (= ${binary:Version}), - librust-comrak-0.18.0+xdg-dev (= ${binary:Version}) + librust-comrak-0.20-dev (= ${binary:Version}), + librust-comrak-0.20+arbitrary-dev (= ${binary:Version}), + librust-comrak-0.20+clap-dev (= ${binary:Version}), + librust-comrak-0.20+cli-dev (= ${binary:Version}), + librust-comrak-0.20+default-dev (= ${binary:Version}), + librust-comrak-0.20+emojis-dev (= ${binary:Version}), + librust-comrak-0.20+shell-words-dev (= ${binary:Version}), + librust-comrak-0.20+shortcodes-dev (= ${binary:Version}), + librust-comrak-0.20+syntect-dev (= ${binary:Version}), + librust-comrak-0.20+xdg-dev (= ${binary:Version}), + librust-comrak-0.20.0-dev (= ${binary:Version}), + librust-comrak-0.20.0+arbitrary-dev (= ${binary:Version}), + librust-comrak-0.20.0+clap-dev (= ${binary:Version}), + librust-comrak-0.20.0+cli-dev (= ${binary:Version}), + librust-comrak-0.20.0+default-dev (= ${binary:Version}), + librust-comrak-0.20.0+emojis-dev (= ${binary:Version}), + librust-comrak-0.20.0+shell-words-dev (= ${binary:Version}), + librust-comrak-0.20.0+shortcodes-dev (= ${binary:Version}), + librust-comrak-0.20.0+syntect-dev (= ${binary:Version}), + librust-comrak-0.20.0+xdg-dev (= ${binary:Version}) Description: 100% CommonMark-compatible GitHub Flavored Markdown parser and formatter - Rust source code - This package contains the source for the Rust comrak crate, packaged by - debcargo for use with cargo and dh-cargo. + Source code for Debianized Rust crate "comrak" diff -Nru rust-comrak-0.18.0/debian/copyright.debcargo.hint rust-comrak-0.20.0/debian/copyright.debcargo.hint --- rust-comrak-0.18.0/debian/copyright.debcargo.hint 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/copyright.debcargo.hint 2024-01-05 08:57:59.000000000 +0000 @@ -37,8 +37,8 @@ Files: debian/* Copyright: - 2023 Debian Rust Maintainers - 2023 Arnaud Ferraris + 2023-2024 Debian Rust Maintainers + 2023-2024 Arnaud Ferraris License: BSD-2-Clause License: BSD-2-Clause diff -Nru rust-comrak-0.18.0/debian/patches/relax-deps.patch rust-comrak-0.20.0/debian/patches/relax-deps.patch --- rust-comrak-0.18.0/debian/patches/relax-deps.patch 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/patches/relax-deps.patch 2024-01-05 08:57:59.000000000 +0000 @@ -2,7 +2,7 @@ =================================================================== --- comrak.orig/Cargo.toml +++ comrak/Cargo.toml -@@ -79,7 +79,7 @@ optional = true +@@ -89,7 +89,7 @@ optional = true version = "0.1.4" [dependencies.typed-arena] diff -Nru rust-comrak-0.18.0/debian/patches/relax-emoji.diff rust-comrak-0.20.0/debian/patches/relax-emoji.diff --- rust-comrak-0.18.0/debian/patches/relax-emoji.diff 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/debian/patches/relax-emoji.diff 2024-01-05 08:57:59.000000000 +0000 @@ -0,0 +1,13 @@ +Index: comrak/Cargo.toml +=================================================================== +--- comrak.orig/Cargo.toml ++++ comrak/Cargo.toml +@@ -66,7 +66,7 @@ optional = true + version = "0.12.0" + + [dependencies.emojis] +-version = "0.5.2" ++version = "0" + optional = true + + [dependencies.entities] diff -Nru rust-comrak-0.18.0/debian/patches/series rust-comrak-0.20.0/debian/patches/series --- rust-comrak-0.18.0/debian/patches/series 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/patches/series 2024-01-05 08:57:59.000000000 +0000 @@ -1 +1,2 @@ relax-deps.patch +relax-emoji.diff diff -Nru rust-comrak-0.18.0/debian/tests/control rust-comrak-0.20.0/debian/tests/control --- rust-comrak-0.18.0/debian/tests/control 2023-09-24 12:33:43.000000000 +0000 +++ rust-comrak-0.20.0/debian/tests/control 2024-01-05 08:57:59.000000000 +0000 @@ -1,54 +1,54 @@ -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --all-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --all-features Features: test-name=rust-comrak:@ Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features arbitrary +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features arbitrary Features: test-name=librust-comrak-dev:arbitrary Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features clap +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features clap Features: test-name=librust-comrak-dev:clap Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features cli +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features cli Features: test-name=librust-comrak-dev:cli Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets Features: test-name=librust-comrak-dev:default Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features emojis +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features emojis Features: test-name=librust-comrak-dev:emojis Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features shell-words +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features shell-words Features: test-name=librust-comrak-dev:shell-words Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features shortcodes +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features shortcodes Features: test-name=librust-comrak-dev:shortcodes Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features syntect +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features syntect Features: test-name=librust-comrak-dev:syntect Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features --features xdg +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features --features xdg Features: test-name=librust-comrak-dev:xdg Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.18.0 --all-targets --no-default-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test comrak 0.20.0 --all-targets --no-default-features Features: test-name=librust-comrak-dev: Depends: dh-cargo (>= 18), librust-ntest-0.9+default-dev, librust-propfuzz-0.0.1+default-dev, @ Restrictions: allow-stderr, skip-not-installable diff -Nru rust-comrak-0.18.0/examples/custom_headings.rs rust-comrak-0.20.0/examples/custom_headings.rs --- rust-comrak-0.18.0/examples/custom_headings.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/custom_headings.rs 2006-07-24 01:21:28.000000000 +0000 @@ -2,14 +2,14 @@ adapters::{HeadingAdapter, HeadingMeta}, markdown_to_html_with_plugins, nodes::Sourcepos, - ComrakOptions, ComrakPlugins, + Options, Plugins, }; use std::io::{self, Write}; fn main() { let adapter = CustomHeadingAdapter; - let mut options = ComrakOptions::default(); - let mut plugins = ComrakPlugins::default(); + let mut options = Options::default(); + let mut plugins = Plugins::default(); plugins.render.heading_adapter = Some(&adapter); print_html( @@ -62,7 +62,7 @@ } } -fn print_html(document: &str, options: &ComrakOptions, plugins: &ComrakPlugins) { +fn print_html(document: &str, options: &Options, plugins: &Plugins) { let html = markdown_to_html_with_plugins(document, options, plugins); println!("{}", html); } diff -Nru rust-comrak-0.18.0/examples/headers.rs rust-comrak-0.20.0/examples/headers.rs --- rust-comrak-0.18.0/examples/headers.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/headers.rs 2006-07-24 01:21:28.000000000 +0000 @@ -2,7 +2,7 @@ use comrak::{ nodes::{AstNode, NodeCode, NodeValue}, - parse_document, Arena, ComrakOptions, + parse_document, Arena, Options, }; fn main() { @@ -13,7 +13,7 @@ fn get_document_title(document: &str) -> String { let arena = Arena::new(); - let root = parse_document(&arena, document, &ComrakOptions::default()); + let root = parse_document(&arena, document, &Options::default()); for node in root.children() { let header = match node.data.clone().into_inner().value { diff -Nru rust-comrak-0.18.0/examples/s-expr.rs rust-comrak-0.20.0/examples/s-expr.rs --- rust-comrak-0.18.0/examples/s-expr.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/s-expr.rs 2006-07-24 01:21:28.000000000 +0000 @@ -14,7 +14,7 @@ const CLOSE_NEWLINE: bool = false; use comrak::nodes::{AstNode, NodeValue}; -use comrak::{parse_document, Arena, ComrakExtensionOptions, ComrakOptions}; +use comrak::{parse_document, Arena, ExtensionOptionsBuilder, Options}; use std::env; use std::error::Error; use std::fs::File; @@ -74,19 +74,21 @@ fn dump(source: &str) -> io::Result<()> { let arena = Arena::new(); - let opts = ComrakOptions { - extension: ComrakExtensionOptions { - strikethrough: true, - tagfilter: true, - table: true, - autolink: true, - tasklist: true, - superscript: true, - footnotes: true, - description_lists: true, - ..ComrakExtensionOptions::default() - }, - ..ComrakOptions::default() + let extension = ExtensionOptionsBuilder::default() + .strikethrough(true) + .tagfilter(true) + .table(true) + .autolink(true) + .tasklist(true) + .superscript(true) + .footnotes(true) + .description_lists(true) + .build() + .unwrap(); + + let opts = Options { + extension, + ..Options::default() }; let doc = parse_document(&arena, source, &opts); diff -Nru rust-comrak-0.18.0/examples/sample.rs rust-comrak-0.20.0/examples/sample.rs --- rust-comrak-0.18.0/examples/sample.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/sample.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,17 +1,17 @@ // Samples used in the README. Wanna make sure they work as advertised. fn small() { - use comrak::{markdown_to_html, ComrakOptions}; + use comrak::{markdown_to_html, Options}; assert_eq!( - markdown_to_html("Hello, **世界**!", &ComrakOptions::default()), + markdown_to_html("Hello, **世界**!", &Options::default()), "

Hello, 世界!

\n" ); } fn large() { use comrak::nodes::{AstNode, NodeValue}; - use comrak::{format_html, parse_document, Arena, ComrakOptions}; + use comrak::{format_html, parse_document, Arena, Options}; // The returned nodes are created in the supplied Arena, and are bound by its lifetime. let arena = Arena::new(); @@ -19,7 +19,7 @@ let root = parse_document( &arena, "This is my input.\n\n1. Also my input.\n2. Certainly my input.\n", - &ComrakOptions::default(), + &Options::default(), ); fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) @@ -40,7 +40,7 @@ }); let mut html = vec![]; - format_html(root, &ComrakOptions::default(), &mut html).unwrap(); + format_html(root, &Options::default(), &mut html).unwrap(); assert_eq!( String::from_utf8(html).unwrap(), diff -Nru rust-comrak-0.18.0/examples/syntax_highlighter.rs rust-comrak-0.20.0/examples/syntax_highlighter.rs --- rust-comrak-0.18.0/examples/syntax_highlighter.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/syntax_highlighter.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,7 +1,7 @@ //! This example shows how to implement a syntax highlighter plugin. use comrak::adapters::SyntaxHighlighterAdapter; -use comrak::{markdown_to_html_with_plugins, ComrakOptions, ComrakPlugins}; +use comrak::{markdown_to_html_with_plugins, Options, Plugins}; use std::collections::HashMap; use std::io::{self, Write}; @@ -59,8 +59,8 @@ fn main() { let adapter = PotatoSyntaxAdapter::new(42); - let options = ComrakOptions::default(); - let mut plugins = ComrakPlugins::default(); + let options = Options::default(); + let mut plugins = Plugins::default(); plugins.render.codefence_syntax_highlighter = Some(&adapter); diff -Nru rust-comrak-0.18.0/examples/syntect.rs rust-comrak-0.20.0/examples/syntect.rs --- rust-comrak-0.18.0/examples/syntect.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/syntect.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,12 +1,17 @@ //! This example shows how to use the bundled syntect plugin. -use comrak::plugins::syntect::SyntectAdapter; -use comrak::{markdown_to_html_with_plugins, ComrakOptions, ComrakPlugins}; +use comrak::plugins::syntect::SyntectAdapterBuilder; +use comrak::{markdown_to_html_with_plugins, Options, Plugins}; fn main() { - let adapter = SyntectAdapter::new("base16-ocean.dark"); - let options = ComrakOptions::default(); - let mut plugins = ComrakPlugins::default(); + run_with(SyntectAdapterBuilder::new().theme("base16-ocean.dark")); + run_with(SyntectAdapterBuilder::new().css()); +} + +fn run_with(builder: SyntectAdapterBuilder) { + let adapter = builder.build(); + let options = Options::default(); + let mut plugins = Plugins::default(); plugins.render.codefence_syntax_highlighter = Some(&adapter); diff -Nru rust-comrak-0.18.0/examples/update-readme.rs rust-comrak-0.20.0/examples/update-readme.rs --- rust-comrak-0.18.0/examples/update-readme.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/examples/update-readme.rs 2006-07-24 01:21:28.000000000 +0000 @@ -4,7 +4,7 @@ use std::str; use comrak::nodes::{AstNode, NodeValue}; -use comrak::{format_commonmark, parse_document, Arena, ComrakOptions}; +use comrak::{format_commonmark, parse_document, Arena, Options}; const DEPENDENCIES: &str = "[dependencies]\ncomrak = "; const HELP: &str = "$ comrak --help\n"; @@ -13,7 +13,7 @@ let arena = Arena::new(); let readme = std::fs::read_to_string("README.md")?; - let doc = parse_document(&arena, &readme, &ComrakOptions::default()); + let doc = parse_document(&arena, &readme, &Options::default()); fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) where @@ -56,7 +56,7 @@ }); let mut out = vec![]; - format_commonmark(doc, &ComrakOptions::default(), &mut out).unwrap(); + format_commonmark(doc, &Options::default(), &mut out).unwrap(); std::fs::write("README.md", &out)?; diff -Nru rust-comrak-0.18.0/flake.lock rust-comrak-0.20.0/flake.lock --- rust-comrak-0.18.0/flake.lock 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/flake.lock 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,198 @@ +{ + "nodes": { + "advisory-db": { + "flake": false, + "locked": { + "lastModified": 1693237955, + "narHash": "sha256-O8bIGaqQmV7jI/fLAFtyCnUDG41qcUoY2TwvxAtcbt4=", + "owner": "rustsec", + "repo": "advisory-db", + "rev": "cbf97de9b780c3279e779ef40b7fa2789b2b9905", + "type": "github" + }, + "original": { + "owner": "rustsec", + "repo": "advisory-db", + "type": "github" + } + }, + "crane": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1693163878, + "narHash": "sha256-HXuyMUVaRSoIA602jfFuYGXt6AMZ+WUxuvLq8iJmYTA=", + "owner": "ipetkov", + "repo": "crane", + "rev": "43db881168bc65b568d36ceb614a0fc8b276191b", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": [] + }, + "locked": { + "lastModified": 1693203690, + "narHash": "sha256-qrgyFtRaduofcJF1T7TscNWM4HTx4qtFzpilD0K8M0o=", + "owner": "nix-community", + "repo": "fenix", + "rev": "8eb35c8c402edda73a7dde82e48bf279542640c7", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1692799911, + "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1701053011, + "narHash": "sha256-8QQ7rFbKFqgKgLoaXVJRh7Ik5LtI3pyBBCfOnNOGkF0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5b528f99f73c4fad127118a8c1126b5e003b01a9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "advisory-db": "advisory-db", + "crane": "crane", + "fenix": "fenix", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "crane", + "flake-utils" + ], + "nixpkgs": [ + "crane", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1691374719, + "narHash": "sha256-HCodqnx1Mi2vN4f3hjRPc7+lSQy18vRn8xWW68GeQOg=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "b520a3889b24aaf909e287d19d406862ced9ffc9", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff -Nru rust-comrak-0.18.0/flake.nix rust-comrak-0.20.0/flake.nix --- rust-comrak-0.18.0/flake.nix 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/flake.nix 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,135 @@ +{ + description = "comrak"; + + inputs = { + nixpkgs.url = github:NixOS/nixpkgs/nixos-23.05; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-analyzer-src.follows = ""; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + advisory-db = { + url = "github:rustsec/advisory-db"; + flake = false; + }; + }; + + outputs = { + self, + nixpkgs, + crane, + fenix, + flake-utils, + advisory-db, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + }; + + inherit (pkgs) lib; + + craneLib = crane.lib.${system}; + src = craneLib.cleanCargoSource (craneLib.path ./.); + + commonArgs = { + inherit src; + + buildInputs = + [ + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + pkgs.libiconv + ]; + }; + + craneLibLLvmTools = + craneLib.overrideToolchain + (fenix.packages.${system}.complete.withComponents [ + "cargo" + "llvm-tools" + "rustc" + ]); + + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + comrak = craneLib.buildPackage (commonArgs + // { + inherit cargoArtifacts; + + doCheck = false; + }); + in { + checks = + { + inherit comrak; + + comrak-clippy = craneLib.cargoClippy (commonArgs + // { + inherit cargoArtifacts; + # cargoClippyExtraArgs = "--lib --bins --examples --tests -- --deny warnings"; + # XXX Not sure if we can fix all these and retain our current MSRV. + cargoClippyExtraArgs = "--lib --bins --examples --tests"; + }); + + comrak-doc = craneLib.cargoDoc (commonArgs + // { + inherit cargoArtifacts; + }); + + comrak-fmt = craneLib.cargoFmt { + inherit src; + }; + + comrak-audit = craneLib.cargoAudit { + inherit src advisory-db; + }; + + comrak-nextest = craneLib.cargoNextest (commonArgs + // { + inherit cargoArtifacts; + partitions = 1; + partitionType = "count"; + }); + } + // lib.optionalAttrs (system == "x86_64-linux") { + comrak-coverage = craneLib.cargoTarpaulin (commonArgs + // { + inherit cargoArtifacts; + }); + }; + + packages = { + default = comrak; + comrak-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs + // { + inherit cargoArtifacts; + }); + }; + + apps.default = flake-utils.lib.mkApp { + drv = comrak; + }; + + formatter = pkgs.alejandra; + + devShells.default = pkgs.mkShell { + inputsFrom = builtins.attrValues self.checks.${system}; + + nativeBuildInputs = with pkgs; [ + cargo + rustc + ]; + }; + }); +} diff -Nru rust-comrak-0.18.0/src/adapters.rs rust-comrak-0.20.0/src/adapters.rs --- rust-comrak-0.18.0/src/adapters.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/adapters.rs 2006-07-24 01:21:28.000000000 +0000 @@ -8,7 +8,7 @@ use crate::nodes::Sourcepos; /// Implement this adapter for creating a plugin for custom syntax highlighting of codefence blocks. -pub trait SyntaxHighlighterAdapter { +pub trait SyntaxHighlighterAdapter: Send + Sync { /// Generates a syntax highlighted HTML output. /// /// lang: Name of the programming language (the info string of the codefence block after the initial "```" part). @@ -57,7 +57,7 @@ /// method defines what's rendered prior the AST content of the heading while the `exit` method /// defines what's rendered after it. Both methods provide access to a [`HeadingMeta`] struct and /// leave the AST content of the heading unchanged. -pub trait HeadingAdapter { +pub trait HeadingAdapter: Send + Sync { /// Render the opening tag. fn enter( &self, diff -Nru rust-comrak-0.18.0/src/arena_tree.rs rust-comrak-0.20.0/src/arena_tree.rs --- rust-comrak-0.18.0/src/arena_tree.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/arena_tree.rs 2006-07-24 01:21:28.000000000 +0000 @@ -7,7 +7,7 @@ Any non-trivial tree involves reference cycles (e.g. if a node has a first child, the parent of the child is that node). To enable this, nodes need to live in an arena allocator -such as `arena::TypedArena` distrubuted with rustc (which is `#[unstable]` as of this writing) +such as `arena::TypedArena` distributed with rustc (which is `#[unstable]` as of this writing) or [`typed_arena::Arena`](https://crates.io/crates/typed-arena). If you need mutability in the node’s `data`, @@ -33,7 +33,7 @@ } /// A simple Debug implementation that prints the children as a tree, without -/// ilooping through the various interior pointer cycles. +/// looping through the various interior pointer cycles. impl<'a, T: 'a> fmt::Debug for Node<'a, T> where T: fmt::Debug, @@ -95,7 +95,7 @@ self.previous_sibling.get() } - /// Return a reference to the previous sibling of this node, unless it is a last child. + /// Return a reference to the next sibling of this node, unless it is a last child. pub fn next_sibling(&self) -> Option<&'a Node<'a, T>> { self.next_sibling.get() } diff -Nru rust-comrak-0.18.0/src/cm.rs rust-comrak-0.20.0/src/cm.rs --- rust-comrak-0.18.0/src/cm.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/cm.rs 2006-07-24 01:21:28.000000000 +0000 @@ -2,14 +2,14 @@ use crate::nodes::TableAlignment; use crate::nodes::{ AstNode, ListDelimType, ListType, NodeCodeBlock, NodeHeading, NodeHtmlBlock, NodeLink, - NodeValue, + NodeTable, NodeValue, }; #[cfg(feature = "shortcodes")] use crate::parser::shortcodes::NodeShortCode; -use crate::parser::ComrakOptions; +use crate::parser::Options; use crate::scanners; use crate::strings::trim_start_match; -use crate::{nodes, ComrakPlugins}; +use crate::{nodes, Plugins}; use std::cmp::max; use std::io::{self, Write}; @@ -17,18 +17,18 @@ /// Formats an AST as CommonMark, modified by the given options. pub fn format_document<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, ) -> io::Result<()> { - format_document_with_plugins(root, options, output, &ComrakPlugins::default()) + format_document_with_plugins(root, options, output, &Plugins::default()) } /// Formats an AST as CommonMark, modified by the given options. Accepts custom plugins. pub fn format_document_with_plugins<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, - _plugins: &ComrakPlugins, + _plugins: &Plugins, ) -> io::Result<()> { let mut f = CommonMarkFormatter::new(root, options); f.format(root); @@ -41,7 +41,7 @@ struct CommonMarkFormatter<'a, 'o> { node: &'a AstNode<'a>, - options: &'o ComrakOptions, + options: &'o Options, v: Vec, prefix: Vec, column: usize, @@ -75,7 +75,7 @@ } impl<'a, 'o> CommonMarkFormatter<'a, 'o> { - fn new(node: &'a AstNode<'a>, options: &'o ComrakOptions) -> Self { + fn new(node: &'a AstNode<'a>, options: &'o Options) -> Self { CommonMarkFormatter { node, options, @@ -308,13 +308,23 @@ self.node = node; let allow_wrap = self.options.render.width > 0 && !self.options.render.hardbreaks; - if !(matches!( - node.data.borrow().value, - NodeValue::Item(..) | NodeValue::TaskItem(..) - ) && node.previous_sibling().is_none() - && entering) - { - self.in_tight_list_item = self.get_in_tight_list_item(node); + let parent_node = node.parent(); + if entering { + if parent_node.is_some() + && matches!( + parent_node.unwrap().data.borrow().value, + NodeValue::Item(..) | NodeValue::TaskItem(..) + ) + { + self.in_tight_list_item = self.get_in_tight_list_item(node); + } + } else if matches!(node.data.borrow().value, NodeValue::List(..)) { + self.in_tight_list_item = parent_node.is_some() + && matches!( + parent_node.unwrap().data.borrow().value, + NodeValue::Item(..) | NodeValue::TaskItem(..) + ) + && self.get_in_tight_list_item(node); } match node.data.borrow().value { @@ -343,7 +353,13 @@ NodeValue::HtmlInline(ref literal) => { self.format_html_inline(literal.as_bytes(), entering) } - NodeValue::Strong => self.format_strong(), + NodeValue::Strong => { + if parent_node.is_none() + || !matches!(parent_node.unwrap().data.borrow().value, NodeValue::Strong) + { + self.format_strong(); + } + } NodeValue::Emph => self.format_emph(node), NodeValue::TaskItem(symbol) => self.format_task_item(symbol, node, entering), NodeValue::Strikethrough => self.format_strikethrough(), @@ -355,9 +371,11 @@ NodeValue::Table(..) => self.format_table(entering), NodeValue::TableRow(..) => self.format_table_row(entering), NodeValue::TableCell => self.format_table_cell(node, entering), - NodeValue::FootnoteDefinition(_) => self.format_footnote_definition(entering), - NodeValue::FootnoteReference(ref r) => { - self.format_footnote_reference(r.as_bytes(), entering) + NodeValue::FootnoteDefinition(ref nfd) => { + self.format_footnote_definition(&nfd.name, entering) + } + NodeValue::FootnoteReference(ref nfr) => { + self.format_footnote_reference(nfr.name.as_bytes(), entering) } }; true @@ -408,13 +426,12 @@ let marker_width = if parent.list_type == ListType::Bullet { 2 } else { - let mut list_number = parent.start; + let list_number = match node.data.borrow().value { + NodeValue::Item(ref ni) => ni.start, + NodeValue::TaskItem(_) => parent.start, + _ => unreachable!(), + }; let list_delim = parent.delimiter; - let mut tmpch = node; - while let Some(tmp) = tmpch.previous_sibling() { - tmpch = tmp; - list_number += 1; - } write!( listmarker, "{}{}{}", @@ -715,7 +732,7 @@ if in_header && node.next_sibling().is_none() { let table = &node.parent().unwrap().parent().unwrap().data.borrow().value; let alignments = match *table { - NodeValue::Table(ref alignments) => alignments, + NodeValue::Table(NodeTable { ref alignments, .. }) => alignments, _ => panic!(), }; @@ -738,11 +755,10 @@ } } } - fn format_footnote_definition(&mut self, entering: bool) { + fn format_footnote_definition(&mut self, name: &str, entering: bool) { if entering { self.footnote_ix += 1; - let footnote_ix = self.footnote_ix; - writeln!(self, "[^{}]:", footnote_ix).unwrap(); + writeln!(self, "[^{}]:", name).unwrap(); write!(self.prefix, " ").unwrap(); } else { let new_len = self.prefix.len() - 4; diff -Nru rust-comrak-0.18.0/src/html.rs rust-comrak-0.20.0/src/html.rs --- rust-comrak-0.18.0/src/html.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/html.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,7 +1,9 @@ //! The HTML renderer for the CommonMark AST, as well as helper functions. use crate::ctype::isspace; -use crate::nodes::{AstNode, ListType, NodeCode, NodeValue, TableAlignment}; -use crate::parser::{ComrakOptions, ComrakPlugins}; +use crate::nodes::{ + AstNode, ListType, NodeCode, NodeFootnoteDefinition, NodeTable, NodeValue, TableAlignment, +}; +use crate::parser::{Options, Plugins}; use crate::scanners; use once_cell::sync::Lazy; use regex::Regex; @@ -16,18 +18,18 @@ /// Formats an AST as HTML, modified by the given options. pub fn format_document<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, ) -> io::Result<()> { - format_document_with_plugins(root, options, output, &ComrakPlugins::default()) + format_document_with_plugins(root, options, output, &Plugins::default()) } /// Formats an AST as HTML, modified by the given options. Accepts custom plugins. pub fn format_document_with_plugins<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, - plugins: &ComrakPlugins, + plugins: &Plugins, ) -> io::Result<()> { let mut writer = WriteWithLast { output, @@ -105,21 +107,19 @@ static REJECTED_CHARS: Lazy = Lazy::new(|| Regex::new(r"[^\p{L}\p{M}\p{N}\p{Pc} -]").unwrap()); - let mut id = header; - id = id.to_lowercase(); - id = REJECTED_CHARS.replace_all(&id, "").to_string(); - id = id.replace(' ', "-"); + let mut id = header.to_lowercase(); + id = REJECTED_CHARS.replace_all(&id, "").replace(' ', "-"); let mut uniq = 0; id = loop { let anchor = if uniq == 0 { - Cow::from(&*id) + Cow::from(&id) } else { - Cow::from(format!("{}-{}", &id, uniq)) + Cow::from(format!("{}-{}", id, uniq)) }; if !self.0.contains(&*anchor) { - break anchor.to_string(); + break anchor.into_owned(); } uniq += 1; @@ -131,11 +131,11 @@ struct HtmlFormatter<'o> { output: &'o mut WriteWithLast<'o>, - options: &'o ComrakOptions, + options: &'o Options, anchorizer: Anchorizer, footnote_ix: u32, written_footnote_ix: u32, - plugins: &'o ComrakPlugins<'o>, + plugins: &'o Plugins<'o>, } #[rustfmt::skip] @@ -365,11 +365,7 @@ } impl<'o> HtmlFormatter<'o> { - fn new( - options: &'o ComrakOptions, - output: &'o mut WriteWithLast<'o>, - plugins: &'o ComrakPlugins, - ) -> Self { + fn new(options: &'o Options, output: &'o mut WriteWithLast<'o>, plugins: &'o Plugins) -> Self { HtmlFormatter { options, output, @@ -703,13 +699,13 @@ self.render_sourcepos(node)?; self.output.write_all(b">")?; } else { - if matches!( - node.parent().unwrap().data.borrow().value, - NodeValue::FootnoteDefinition(..) - ) && node.next_sibling().is_none() + if let NodeValue::FootnoteDefinition(nfd) = + &node.parent().unwrap().data.borrow().value { - self.output.write_all(b" ")?; - self.put_footnote_backref()?; + if node.next_sibling().is_none() { + self.output.write_all(b" ")?; + self.put_footnote_backref(nfd)?; + } } self.output.write_all(b"

\n")?; } @@ -764,12 +760,17 @@ } } NodeValue::Strong => { - if entering { - self.output.write_all(b"")?; - } else { - self.output.write_all(b"")?; + let parent_node = node.parent(); + if parent_node.is_none() + || !matches!(parent_node.unwrap().data.borrow().value, NodeValue::Strong) + { + if entering { + self.output.write_all(b"")?; + } else { + self.output.write_all(b"")?; + } } } NodeValue::Emph => { @@ -892,7 +893,7 @@ let table = &node.parent().unwrap().parent().unwrap().data.borrow().value; let alignments = match *table { - NodeValue::Table(ref alignments) => alignments, + NodeValue::Table(NodeTable { ref alignments, .. }) => alignments, _ => panic!(), }; @@ -933,7 +934,7 @@ self.output.write_all(b"")?; } } - NodeValue::FootnoteDefinition(_) => { + NodeValue::FootnoteDefinition(ref nfd) => { if entering { if self.footnote_ix == 0 { self.output.write_all(b"", self.footnote_ix)?; + self.output.write_all(b" id=\"fn-")?; + self.escape_href(nfd.name.as_bytes())?; + self.output.write_all(b"\">")?; } else { - if self.put_footnote_backref()? { + if self.put_footnote_backref(nfd)? { self.output.write_all(b"\n")?; } self.output.write_all(b"\n")?; } } - NodeValue::FootnoteReference(ref r) => { + NodeValue::FootnoteReference(ref nfr) => { if entering { + let mut ref_id = format!("fnref-{}", nfr.name); + self.output.write_all(b"{}", - r, r, r - )?; + + if nfr.ref_num > 1 { + ref_id = format!("{}-{}", ref_id, nfr.ref_num); + } + + self.output + .write_all(b" class=\"footnote-ref\">{}", nfr.ix)?; } } NodeValue::TaskItem(symbol) => { @@ -970,7 +982,7 @@ self.output.write_all(b">")?; write!( self.output, - " ", + " ", if symbol.is_some() { "checked=\"\" " } else { @@ -995,17 +1007,31 @@ Ok(()) } - fn put_footnote_backref(&mut self) -> io::Result { + fn put_footnote_backref(&mut self, nfd: &NodeFootnoteDefinition) -> io::Result { if self.written_footnote_ix >= self.footnote_ix { return Ok(false); } self.written_footnote_ix = self.footnote_ix; - write!( - self.output, - "", - self.footnote_ix - )?; + + let mut ref_suffix = String::new(); + let mut superscript = String::new(); + + for ref_num in 1..=nfd.total_references { + if ref_num > 1 { + ref_suffix = format!("-{}", ref_num); + superscript = format!("{}", ref_num); + write!(self.output, " ")?; + } + + self.output.write_all(b"↩{}", + ref_suffix, self.footnote_ix, ref_suffix, self.footnote_ix, ref_suffix, superscript + )?; + } Ok(true) } } diff -Nru rust-comrak-0.18.0/src/lib.rs rust-comrak-0.20.0/src/lib.rs --- rust-comrak-0.18.0/src/lib.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/lib.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,14 +1,14 @@ //! A 100% [CommonMark](http://commonmark.org/) and [GFM](https://github.github.com/gfm/) //! compatible Markdown parser. Source repository is at . //! -//! The design is based on [cmark](https://github.com/github/cmark), so familiarity with that will -//! help. +//! The design is based on [cmark-gfm](https://github.com/github/cmark-gfm), so +//! familiarity with that will help. //! //! You can use `comrak::markdown_to_html` directly: //! //! ``` -//! use comrak::{markdown_to_html, ComrakOptions}; -//! assert_eq!(markdown_to_html("Hello, **世界**!", &ComrakOptions::default()), +//! use comrak::{markdown_to_html, Options}; +//! assert_eq!(markdown_to_html("Hello, **世界**!", &Options::default()), //! "

Hello, 世界!

\n"); //! ``` //! @@ -16,7 +16,7 @@ //! formatter: //! //! ``` -//! use comrak::{Arena, parse_document, format_html, ComrakOptions}; +//! use comrak::{Arena, parse_document, format_html, Options}; //! use comrak::nodes::{AstNode, NodeValue}; //! //! # fn main() { @@ -26,7 +26,7 @@ //! let root = parse_document( //! &arena, //! "This is my input.\n\n1. Also my input.\n2. Certainly my input.\n", -//! &ComrakOptions::default()); +//! &Options::default()); //! //! fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) //! where F : Fn(&'a AstNode<'a>) { @@ -47,7 +47,7 @@ //! }); //! //! let mut html = vec![]; -//! format_html(root, &ComrakOptions::default(), &mut html).unwrap(); +//! format_html(root, &Options::default(), &mut html).unwrap(); //! //! assert_eq!( //! String::from_utf8(html).unwrap(), @@ -59,6 +59,7 @@ //! # } //! ``` +#![cfg_attr(docsrs, feature(doc_cfg))] #![deny( missing_docs, missing_debug_implementations, @@ -99,29 +100,38 @@ pub use html::format_document_with_plugins as format_html_with_plugins; pub use html::Anchorizer; pub use parser::{ - parse_document, parse_document_with_broken_link_callback, ComrakExtensionOptions, - ComrakOptions, ComrakParseOptions, ComrakPlugins, ComrakRenderOptions, ComrakRenderPlugins, - ListStyleType, + parse_document, parse_document_with_broken_link_callback, ExtensionOptions, + ExtensionOptionsBuilder, ListStyleType, Options, ParseOptions, ParseOptionsBuilder, Plugins, + PluginsBuilder, RenderOptions, RenderOptionsBuilder, RenderPlugins, RenderPluginsBuilder, }; pub use typed_arena::Arena; pub use xml::format_document as format_xml; pub use xml::format_document_with_plugins as format_xml_with_plugins; +/// Legacy naming of [`ExtensionOptions`] +pub type ComrakExtensionOptions = ExtensionOptions; +/// Legacy naming of [`Options`] +pub type ComrakOptions = Options; +/// Legacy naming of [`ParseOptions`] +pub type ComrakParseOptions = ParseOptions; +/// Legacy naming of [`Plugins`] +pub type ComrakPlugins<'a> = Plugins<'a>; +/// Legacy naming of [`RenderOptions`] +pub type ComrakRenderOptions = RenderOptions; +/// Legacy naming of [`RenderPlugins`] +pub type ComrakRenderPlugins<'a> = RenderPlugins<'a>; + /// Render Markdown to HTML. /// /// See the documentation of the crate root for an example. -pub fn markdown_to_html(md: &str, options: &ComrakOptions) -> String { - markdown_to_html_with_plugins(md, options, &ComrakPlugins::default()) +pub fn markdown_to_html(md: &str, options: &Options) -> String { + markdown_to_html_with_plugins(md, options, &Plugins::default()) } /// Render Markdown to HTML using plugins. /// /// See the documentation of the crate root for an example. -pub fn markdown_to_html_with_plugins( - md: &str, - options: &ComrakOptions, - plugins: &ComrakPlugins, -) -> String { +pub fn markdown_to_html_with_plugins(md: &str, options: &Options, plugins: &Plugins) -> String { let arena = Arena::new(); let root = parse_document(&arena, md, options); let mut bw = BufWriter::new(Vec::new()); @@ -135,7 +145,7 @@ } /// Render Markdown back to CommonMark. -pub fn markdown_to_commonmark(md: &str, options: &ComrakOptions) -> String { +pub fn markdown_to_commonmark(md: &str, options: &Options) -> String { let arena = Arena::new(); let root = parse_document(&arena, md, options); let mut bw = BufWriter::new(Vec::new()); @@ -145,16 +155,16 @@ /// Render Markdown to CommonMark XML. /// See https://github.com/commonmark/commonmark-spec/blob/master/CommonMark.dtd. -pub fn markdown_to_commonmark_xml(md: &str, options: &ComrakOptions) -> String { - markdown_to_commonmark_xml_with_plugins(md, options, &ComrakPlugins::default()) +pub fn markdown_to_commonmark_xml(md: &str, options: &Options) -> String { + markdown_to_commonmark_xml_with_plugins(md, options, &Plugins::default()) } /// Render Markdown to CommonMark XML using plugins. /// See https://github.com/commonmark/commonmark-spec/blob/master/CommonMark.dtd. pub fn markdown_to_commonmark_xml_with_plugins( md: &str, - options: &ComrakOptions, - plugins: &ComrakPlugins, + options: &Options, + plugins: &Plugins, ) -> String { let arena = Arena::new(); let root = parse_document(&arena, md, options); diff -Nru rust-comrak-0.18.0/src/main.rs rust-comrak-0.20.0/src/main.rs --- rust-comrak-0.18.0/src/main.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/main.rs 2006-07-24 01:21:28.000000000 +0000 @@ -2,8 +2,8 @@ use comrak::{ adapters::SyntaxHighlighterAdapter, plugins::syntect::SyntectAdapter, Arena, - ComrakExtensionOptions, ComrakOptions, ComrakParseOptions, ComrakPlugins, ComrakRenderOptions, - ListStyleType, + ExtensionOptionsBuilder, ListStyleType, Options, ParseOptionsBuilder, Plugins, + RenderOptionsBuilder, }; use std::boxed::Box; use std::env; @@ -60,6 +60,10 @@ #[arg(long)] relaxed_tasklist_character: bool, + /// Enable relaxing of autolink parsing, allowing links to be recognized when in brackets + #[arg(long)] + relaxed_autolinks: bool, + /// Default value for fenced code block's info strings if none is given #[arg(long, value_name = "INFO")] default_info_string: Option, @@ -195,47 +199,59 @@ let exts = &cli.extensions; - let options = ComrakOptions { - extension: ComrakExtensionOptions { - strikethrough: exts.contains(&Extension::Strikethrough) || cli.gfm, - tagfilter: exts.contains(&Extension::Tagfilter) || cli.gfm, - table: exts.contains(&Extension::Table) || cli.gfm, - autolink: exts.contains(&Extension::Autolink) || cli.gfm, - tasklist: exts.contains(&Extension::Tasklist) || cli.gfm, - superscript: exts.contains(&Extension::Superscript), - header_ids: cli.header_ids, - footnotes: exts.contains(&Extension::Footnotes), - description_lists: exts.contains(&Extension::DescriptionLists), - front_matter_delimiter: cli.front_matter_delimiter, - #[cfg(feature = "shortcodes")] - shortcodes: cli.gemojis, - }, - parse: ComrakParseOptions { - smart: cli.smart, - default_info_string: cli.default_info_string, - relaxed_tasklist_matching: cli.relaxed_tasklist_character, - }, - render: ComrakRenderOptions { - hardbreaks: cli.hardbreaks, - github_pre_lang: cli.github_pre_lang || cli.gfm, - full_info_string: cli.full_info_string, - width: cli.width, - unsafe_: cli.unsafe_, - escape: cli.escape, - list_style: cli.list_style.into(), - sourcepos: cli.sourcepos, - }, + let mut extension = ExtensionOptionsBuilder::default(); + extension + .strikethrough(exts.contains(&Extension::Strikethrough) || cli.gfm) + .tagfilter(exts.contains(&Extension::Tagfilter) || cli.gfm) + .table(exts.contains(&Extension::Table) || cli.gfm) + .autolink(exts.contains(&Extension::Autolink) || cli.gfm) + .tasklist(exts.contains(&Extension::Tasklist) || cli.gfm) + .superscript(exts.contains(&Extension::Superscript)) + .header_ids(cli.header_ids) + .footnotes(exts.contains(&Extension::Footnotes)) + .description_lists(exts.contains(&Extension::DescriptionLists)) + .front_matter_delimiter(cli.front_matter_delimiter); + + #[cfg(feature = "shortcodes")] + { + extension.shortcodes(cli.gemojis); + } + + let extension = extension.build()?; + + let parse = ParseOptionsBuilder::default() + .smart(cli.smart) + .default_info_string(cli.default_info_string) + .relaxed_tasklist_matching(cli.relaxed_tasklist_character) + .relaxed_autolinks(cli.relaxed_autolinks) + .build()?; + + let render = RenderOptionsBuilder::default() + .hardbreaks(cli.hardbreaks) + .github_pre_lang(cli.github_pre_lang || cli.gfm) + .full_info_string(cli.full_info_string) + .width(cli.width) + .unsafe_(cli.unsafe_) + .escape(cli.escape) + .list_style(cli.list_style.into()) + .sourcepos(cli.sourcepos) + .build()?; + + let options = Options { + extension, + parse, + render, }; let syntax_highlighter: Option<&dyn SyntaxHighlighterAdapter>; - let mut plugins: ComrakPlugins = ComrakPlugins::default(); + let mut plugins: Plugins = Plugins::default(); let adapter: SyntectAdapter; let theme = cli.syntax_highlighting; if theme.is_empty() || theme == "none" { syntax_highlighter = None; } else { - adapter = SyntectAdapter::new(&theme); + adapter = SyntectAdapter::new(Some(&theme)); syntax_highlighter = Some(&adapter); } diff -Nru rust-comrak-0.18.0/src/nodes.rs rust-comrak-0.20.0/src/nodes.rs --- rust-comrak-0.18.0/src/nodes.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/nodes.rs 2006-07-24 01:21:28.000000000 +0000 @@ -8,7 +8,7 @@ use crate::parser::shortcodes::NodeShortCode; /// The core AST node enum. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NodeValue { /// The root of every CommonMark document. Contains **blocks**. Document, @@ -88,11 +88,11 @@ /// **Block**. A footnote definition. The `String` is the footnote's name. /// Contains other **blocks**. - FootnoteDefinition(String), + FootnoteDefinition(NodeFootnoteDefinition), /// **Block**. A [table](https://github.github.com/gfm/#tables-extension-) per the GFM spec. /// Contains table rows. - Table(Vec), + Table(NodeTable), /// **Block**. A table row. The `bool` represents whether the row is the header row or not. /// Contains table cells. @@ -111,7 +111,7 @@ TaskItem(Option), /// **Inline**. A [soft line break](https://github.github.com/gfm/#soft-line-breaks). If - /// the `hardbreaks` option is set in `ComrakOptions` during formatting, it will be formatted + /// the `hardbreaks` option is set in `Options` during formatting, it will be formatted /// as a `LineBreak`. SoftBreak, @@ -145,8 +145,8 @@ /// **Inline**. An [image](https://github.github.com/gfm/#images). Image(NodeLink), - /// **Inline**. A footnote reference; the `String` is the referent footnote's name. - FootnoteReference(String), + /// **Inline**. A footnote reference. + FootnoteReference(NodeFootnoteReference), #[cfg(feature = "shortcodes")] /// **Inline**. An Emoji character generated from a shortcode. Enable with feature "shortcodes". @@ -154,7 +154,7 @@ } /// Alignment of a single table cell. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum TableAlignment { /// Cell content is unaligned. None, @@ -180,8 +180,24 @@ } } +/// The metadata of a table +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct NodeTable { + /// The table alignments + pub alignments: Vec, + + /// Number of columns of the table + pub num_columns: usize, + + /// Number of rows of the table + pub num_rows: usize, + + /// Number of non-empty, non-autocompleted cells + pub num_nonempty_cells: usize, +} + /// An inline [code span](https://github.github.com/gfm/#code-spans). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeCode { /// The URL for the link destination or image source. pub num_backticks: usize, @@ -194,7 +210,7 @@ } /// The details of a link's destination, or an image's source. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeLink { /// The URL for the link destination or image source. pub url: String, @@ -207,7 +223,7 @@ } /// The metadata of a list; the kind of list, the delimiter used and so on. -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct NodeList { /// The kind of list (bullet (unordered) or ordered). pub list_type: ListType, @@ -233,7 +249,7 @@ } /// The metadata of a description list -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct NodeDescriptionItem { /// Number of spaces before the list marker. pub marker_offset: usize, @@ -243,7 +259,7 @@ } /// The type of list. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ListType { /// A bullet list, i.e. an unordered list. Bullet, @@ -259,7 +275,7 @@ } /// The delimiter for ordered lists, i.e. the character which appears after each number. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ListDelimType { /// A period character `.`. Period, @@ -284,7 +300,7 @@ } /// The metadata and data of a code block (fenced or indented). -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct NodeCodeBlock { /// Whether the code block is fenced. pub fenced: bool, @@ -309,7 +325,7 @@ } /// The metadata of a heading. -#[derive(Default, Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct NodeHeading { /// The level of the header; from 1 to 6 for ATX headings, 1 or 2 for setext headings. pub level: u8, @@ -319,7 +335,7 @@ } /// The metadata of an included HTML block. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct NodeHtmlBlock { /// The HTML block's type pub block_type: u8, @@ -329,6 +345,29 @@ pub literal: String, } +/// The metadata of a footnote definition. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct NodeFootnoteDefinition { + /// The name of the footnote. + pub name: String, + + /// Total number of references to this footnote + pub total_references: u32, +} + +/// The metadata of a footnote reference. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct NodeFootnoteReference { + /// The name of the footnote. + pub name: String, + + /// The index of reference to the same footnote + pub ref_num: u32, + + /// The index of the footnote in the document. + pub ix: u32, +} + impl NodeValue { /// Indicates whether this node is a block node or inline node. pub fn block(&self) -> bool { @@ -422,7 +461,7 @@ NodeValue::FrontMatter(_) => "frontmatter", NodeValue::TaskItem { .. } => "taskitem", NodeValue::Superscript => "superscript", - NodeValue::FootnoteReference(_) => "footnote_reference", + NodeValue::FootnoteReference(..) => "footnote_reference", #[cfg(feature = "shortcodes")] NodeValue::ShortCode(_) => "shortcode", } @@ -433,7 +472,7 @@ /// /// The struct contains metadata about the node's position in the original document, and the core /// enum, `NodeValue`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Ast { /// The node value itself. pub value: NodeValue, diff -Nru rust-comrak-0.18.0/src/parser/autolink.rs rust-comrak-0.20.0/src/parser/autolink.rs --- rust-comrak-0.18.0/src/parser/autolink.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/parser/autolink.rs 2006-07-24 01:21:28.000000000 +0000 @@ -10,6 +10,7 @@ arena: &'a Arena>, node: &'a AstNode<'a>, contents_str: &mut String, + relaxed_autolinks: bool, ) { let contents = contents_str.as_bytes(); let len = contents.len(); @@ -17,8 +18,27 @@ while i < len { let mut post_org = None; + let mut bracket_opening = 0; + // cmark-gfm ignores links inside brackets, such as `[[http://example.com]` while i < len { + if !relaxed_autolinks { + match contents[i] { + b'[' => { + bracket_opening += 1; + } + b']' => { + bracket_opening -= 1; + } + _ => (), + } + + if bracket_opening > 0 { + i += 1; + continue; + } + } + match contents[i] { b':' => { post_org = url_match(arena, contents, i); @@ -153,7 +173,9 @@ fn autolink_delim(data: &[u8], mut link_end: usize) -> usize { static LINK_END_ASSORTMENT: Lazy<[bool; 256]> = Lazy::new(|| { let mut sc = [false; 256]; - for c in &[b'?', b'!', b'.', b',', b':', b'*', b'_', b'~', b'\'', b'"'] { + for c in &[ + b'?', b'!', b'.', b',', b':', b'*', b'_', b'~', b'\'', b'"', b'[', b']', + ] { sc[*c as usize] = true; } sc @@ -278,6 +300,8 @@ let size = contents.len(); + let mut auto_mailto = true; + let mut is_xmpp = false; let mut rewind = 0; while rewind < i { @@ -288,6 +312,21 @@ continue; } + if c == b':' { + if validate_protocol("mailto", contents, i - rewind - 1) { + auto_mailto = false; + rewind += 1; + continue; + } + + if validate_protocol("xmpp", contents, i - rewind - 1) { + is_xmpp = true; + auto_mailto = false; + rewind += 1; + continue; + } + } + break; } @@ -307,6 +346,8 @@ return None; } else if c == b'.' && link_end < size - i - 1 && isalnum(contents[i + link_end + 1]) { np += 1; + } else if c == b'/' && is_xmpp { + // xmpp allows a `/` in the url } else if c != b'-' && c != b'_' { break; } @@ -326,7 +367,11 @@ return None; } - let mut url = "mailto:".to_string(); + let mut url = if auto_mailto { + "mailto:".to_string() + } else { + "".to_string() + }; let text = str::from_utf8(&contents[i - rewind..link_end + i]).unwrap(); url.push_str(text); @@ -346,3 +391,15 @@ )); Some((inl, rewind, rewind + link_end)) } + +fn validate_protocol(protocol: &str, contents: &[u8], cursor: usize) -> bool { + let size = contents.len(); + let mut rewind = 0; + + while rewind < cursor && isalpha(contents[cursor - rewind - 1]) { + rewind += 1; + } + + size - cursor + rewind >= protocol.len() + && &contents[cursor - rewind..cursor] == protocol.as_bytes() +} diff -Nru rust-comrak-0.18.0/src/parser/inlines.rs rust-comrak-0.20.0/src/parser/inlines.rs --- rust-comrak-0.18.0/src/parser/inlines.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/parser/inlines.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,14 +1,13 @@ use crate::arena_tree::Node; use crate::ctype::{ispunct, isspace}; use crate::entity; -use crate::nodes::{Ast, AstNode, NodeCode, NodeLink, NodeValue, Sourcepos}; +use crate::nodes::{Ast, AstNode, NodeCode, NodeFootnoteReference, NodeLink, NodeValue, Sourcepos}; #[cfg(feature = "shortcodes")] use crate::parser::shortcodes::NodeShortCode; -use crate::parser::{ - unwrap_into_2, unwrap_into_copy, AutolinkType, Callback, ComrakOptions, Reference, -}; +use crate::parser::{unwrap_into_2, unwrap_into_copy, AutolinkType, Callback, Options, Reference}; use crate::scanners; use crate::strings; +use crate::strings::Case; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::convert::TryFrom; @@ -22,7 +21,7 @@ pub struct Subject<'a: 'd, 'r, 'o, 'd, 'i, 'c: 'subj, 'subj> { pub arena: &'a Arena>, - options: &'o ComrakOptions, + options: &'o Options, pub input: &'i [u8], line: usize, pub pos: usize, @@ -106,7 +105,7 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> { pub fn new( arena: &'a Arena>, - options: &'o ComrakOptions, + options: &'o Options, input: &'i [u8], line: usize, block_offset: usize, @@ -462,12 +461,7 @@ // At this point the entire delimiter stack from `stack_bottom` up has // been scanned for matches, everything left is just text. Pop it all // off. - while self - .last_delimiter - .map_or(false, |d| d.position >= stack_bottom) - { - self.remove_delimiter(self.last_delimiter.unwrap()); - } + self.remove_delimiters(stack_bottom); } fn remove_delimiter(&mut self, delimiter: &'d Delimiter<'a, 'd>) { @@ -482,6 +476,15 @@ } } + fn remove_delimiters(&mut self, stack_bottom: usize) { + while self + .last_delimiter + .map_or(false, |d| d.position >= stack_bottom) + { + self.remove_delimiter(self.last_delimiter.unwrap()); + } + } + #[inline] pub fn eof(&self) -> bool { self.pos >= self.input.len() @@ -1197,7 +1200,7 @@ } // Need to normalize both to lookup in refmap and to call callback - let lab = strings::normalize_label(&lab); + let lab = strings::normalize_label(&lab, Case::DontPreserve); let mut reff = if found_label { self.refmap.lookup(&lab) } else { @@ -1216,43 +1219,81 @@ return None; } - let mut text: Option = None; + let bracket_inl_text = self.brackets[brackets_len - 1].inl_text; + if self.options.extension.footnotes - && match self.brackets[brackets_len - 1].inl_text.next_sibling() { + && match bracket_inl_text.next_sibling() { Some(n) => { - text = n.data.borrow().value.text().cloned(); - text.is_some() && n.next_sibling().is_none() + if n.data.borrow().value.text().is_some() { + n.data + .borrow() + .value + .text() + .unwrap() + .as_bytes() + .starts_with(&[b'^']) + } else { + false + } } _ => false, } { - let text = text.unwrap(); - if text.len() > 1 && text.as_bytes()[0] == b'^' { + let mut text = String::new(); + let mut sibling_iterator = bracket_inl_text.following_siblings(); + + self.pos = initial_pos; + + // Skip the initial node, which holds the `[` + sibling_iterator.next().unwrap(); + + // The footnote name could have been parsed into multiple text/htmlinline nodes. + // For example `[^_foo]` gives `^`, `_`, and `foo`. So pull them together. + // Since we're handling the closing bracket, the only siblings at this point are + // related to the footnote name. + for sibling in sibling_iterator { + match sibling.data.borrow().value { + NodeValue::Text(ref literal) | NodeValue::HtmlInline(ref literal) => { + text.push_str(literal); + } + _ => {} + }; + } + + if text.len() > 1 { let inl = self.make_inline( - NodeValue::FootnoteReference(text[1..].to_string()), + NodeValue::FootnoteReference(NodeFootnoteReference { + name: text[1..].to_string(), + ref_num: 0, + ix: 0, + }), // Overridden immediately below. self.pos, self.pos, ); - inl.data.borrow_mut().sourcepos.start.column = self.brackets[brackets_len - 1] - .inl_text - .data - .borrow() - .sourcepos - .start - .column; + inl.data.borrow_mut().sourcepos.start.column = + bracket_inl_text.data.borrow().sourcepos.start.column; inl.data.borrow_mut().sourcepos.end.column = usize::try_from( self.pos as isize + self.column_offset + self.block_offset as isize, ) .unwrap(); - self.brackets[brackets_len - 1].inl_text.insert_before(inl); - self.brackets[brackets_len - 1] - .inl_text - .next_sibling() - .unwrap() - .detach(); - self.brackets[brackets_len - 1].inl_text.detach(); - self.process_emphasis(self.brackets[brackets_len - 1].position); + bracket_inl_text.insert_before(inl); + + // detach all the nodes, including bracket_inl_text + sibling_iterator = bracket_inl_text.following_siblings(); + for sibling in sibling_iterator { + match sibling.data.borrow().value { + NodeValue::Text(_) | NodeValue::HtmlInline(_) => { + sibling.detach(); + } + _ => {} + }; + } + + // We don't need to process emphasis for footnote names, so cleanup + // any outstanding delimiters + self.remove_delimiters(self.brackets[brackets_len - 1].position); + self.brackets.pop(); return None; } diff -Nru rust-comrak-0.18.0/src/parser/mod.rs rust-comrak-0.20.0/src/parser/mod.rs --- rust-comrak-0.18.0/src/parser/mod.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/parser/mod.rs 2006-07-24 01:21:28.000000000 +0000 @@ -8,13 +8,14 @@ use crate::arena_tree::Node; use crate::ctype::{isdigit, isspace}; use crate::entity; -use crate::nodes::{self, Sourcepos}; +use crate::nodes::{self, NodeFootnoteDefinition, Sourcepos}; use crate::nodes::{ Ast, AstNode, ListDelimType, ListType, NodeCodeBlock, NodeDescriptionItem, NodeHeading, NodeHtmlBlock, NodeList, NodeValue, }; use crate::scanners; -use crate::strings::{self, split_off_front_matter}; +use crate::strings::{self, split_off_front_matter, Case}; +use derive_builder::Builder; use std::cell::RefCell; use std::cmp::min; use std::collections::HashMap; @@ -30,6 +31,12 @@ const TAB_STOP: usize = 4; const CODE_INDENT: usize = 4; +// Very deeply nested lists can cause quadratic performance issues. +// This constant is used in open_new_blocks() to limit the nesting +// depth. It is unlikely that a non-contrived markdown document will +// be nested this deeply. +const MAX_LIST_DEPTH: usize = 100; + macro_rules! node_matches { ($node:expr, $( $pat:pat )|+) => {{ matches!( @@ -45,7 +52,7 @@ pub fn parse_document<'a>( arena: &'a Arena>, buffer: &str, - options: &ComrakOptions, + options: &Options, ) -> &'a AstNode<'a> { parse_document_with_broken_link_callback(arena, buffer, options, None) } @@ -61,7 +68,7 @@ /// described in the [GFM spec](https://github.github.com/gfm/#matches). /// /// ``` -/// use comrak::{Arena, parse_document_with_broken_link_callback, format_html, ComrakOptions}; +/// use comrak::{Arena, parse_document_with_broken_link_callback, format_html, Options}; /// use comrak::nodes::{AstNode, NodeValue}; /// /// # fn main() -> std::io::Result<()> { @@ -71,7 +78,7 @@ /// let root = parse_document_with_broken_link_callback( /// &arena, /// "# Cool input!\nWow look at this cool [link][foo]. A [broken link] renders as text.", -/// &ComrakOptions::default(), +/// &Options::default(), /// Some(&mut |link_ref: &str| match link_ref { /// "foo" => Some(( /// "https://www.rust-lang.org/".to_string(), @@ -82,7 +89,7 @@ /// ); /// /// let mut output = Vec::new(); -/// format_html(root, &ComrakOptions::default(), &mut output)?; +/// format_html(root, &Options::default(), &mut output)?; /// let output_str = std::str::from_utf8(&output).expect("invalid UTF-8"); /// assert_eq!(output_str, "

Cool input!

\n

Wow look at this cool \ /// link. \ @@ -93,7 +100,7 @@ pub fn parse_document_with_broken_link_callback<'a, 'c>( arena: &'a Arena>, buffer: &str, - options: &ComrakOptions, + options: &Options, callback: Option>, ) -> &'a AstNode<'a> { let root: &'a AstNode<'a> = arena.alloc(Node::new(RefCell::new(Ast { @@ -132,35 +139,37 @@ last_line_length: usize, last_buffer_ended_with_cr: bool, total_size: usize, - options: &'o ComrakOptions, + options: &'o Options, callback: Option>, } #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] /// Umbrella options struct. -pub struct ComrakOptions { +pub struct Options { /// Enable CommonMark extensions. - pub extension: ComrakExtensionOptions, + pub extension: ExtensionOptions, /// Configure parse-time options. - pub parse: ComrakParseOptions, + pub parse: ParseOptions, /// Configure render-time options. - pub render: ComrakRenderOptions, + pub render: RenderOptions, } -#[derive(Default, Debug, Clone)] +#[non_exhaustive] +#[derive(Default, Debug, Clone, Builder)] +#[builder(default)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] /// Options to select extensions. -pub struct ComrakExtensionOptions { +pub struct ExtensionOptions { /// Enables the /// [strikethrough extension](https://github.github.com/gfm/#strikethrough-extension-) /// from the GFM spec. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.strikethrough = true; /// assert_eq!(markdown_to_html("Hello ~world~ there.\n", &options), /// "

Hello world there.

\n"); @@ -172,8 +181,8 @@ /// from the GFM spec. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.tagfilter = true; /// options.render.unsafe_ = true; /// assert_eq!(markdown_to_html("Hello .\n\n<xmp>", &options), @@ -185,8 +194,8 @@ /// from the GFM spec. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.table = true; /// assert_eq!(markdown_to_html("| a | b |\n|---|---|\n| c | d |\n", &options), /// "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n\ @@ -198,8 +207,8 @@ /// from the GFM spec. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.autolink = true; /// assert_eq!(markdown_to_html("Hello www.github.com.\n", &options), /// "<p>Hello <a href=\"http://www.github.com\">www.github.com</a>.</p>\n"); @@ -214,12 +223,12 @@ /// rendered. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.tasklist = true; /// options.render.unsafe_ = true; /// assert_eq!(markdown_to_html("* [x] Done\n* [ ] Not done\n", &options), - /// "<ul>\n<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Done</li>\n\ + /// "<ul>\n<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Done</li>\n\ /// <li><input type=\"checkbox\" disabled=\"\" /> Not done</li>\n</ul>\n"); /// ``` pub tasklist: bool, @@ -227,8 +236,8 @@ /// Enables the superscript Comrak extension. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.superscript = true; /// assert_eq!(markdown_to_html("e = mc^2^.\n", &options), /// "<p>e = mc<sup>2</sup>.</p>\n"); @@ -238,8 +247,8 @@ /// Enables the header IDs Comrak extension. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.header_ids = Some("user-content-".to_string()); /// assert_eq!(markdown_to_html("# README\n", &options), /// "<h1><a href=\"#readme\" aria-hidden=\"true\" class=\"anchor\" id=\"user-content-readme\"></a>README</h1>\n"); @@ -252,11 +261,11 @@ /// [Kramdown](https://kramdown.gettalong.org/syntax.html#footnotes). /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.footnotes = true; /// assert_eq!(markdown_to_html("Hi[^x].\n\n[^x]: A greeting.\n", &options), - /// "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup>.</p>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-1\">\n<p>A greeting. <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n</li>\n</ol>\n</section>\n"); + /// "<p>Hi<sup class=\"footnote-ref\"><a href=\"#fn-x\" id=\"fnref-x\" data-footnote-ref>1</a></sup>.</p>\n<section class=\"footnotes\" data-footnotes>\n<ol>\n<li id=\"fn-x\">\n<p>A greeting. <a href=\"#fnref-x\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n</li>\n</ol>\n</section>\n"); /// ``` pub footnotes: bool, @@ -280,8 +289,8 @@ /// ``` /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.description_lists = true; /// assert_eq!(markdown_to_html("Term\n\n: Definition", &options), /// "<dl><dt>Term</dt>\n<dd>\n<p>Definition</p>\n</dd>\n</dl>\n"); @@ -306,18 +315,18 @@ /// ``` /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// options.extension.front_matter_delimiter = Some("---".to_owned()); /// assert_eq!( /// markdown_to_html("---\nlayout: post\n---\nText\n", &options), - /// markdown_to_html("Text\n", &ComrakOptions::default())); + /// markdown_to_html("Text\n", &Options::default())); /// ``` /// /// ``` - /// # use comrak::{format_commonmark, Arena, ComrakOptions}; + /// # use comrak::{format_commonmark, Arena, Options}; /// use comrak::parse_document; - /// let mut options = ComrakOptions::default(); + /// let mut options = Options::default(); /// options.extension.front_matter_delimiter = Some("---".to_owned()); /// let arena = Arena::new(); /// let input ="---\nlayout: post\n---\nText\n"; @@ -329,12 +338,12 @@ pub front_matter_delimiter: Option<String>, #[cfg(feature = "shortcodes")] - /// Available if "shortcodes" feature is enabled. Phrases wrapped inside of ':' blocks will be - /// replaced with emojis. + #[cfg_attr(docsrs, doc(cfg(feature = "shortcodes")))] + /// Phrases wrapped inside of ':' blocks will be replaced with emojis. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("Happy Friday! :smile:", &options), /// "<p>Happy Friday! :smile:</p>\n"); /// @@ -345,15 +354,17 @@ pub shortcodes: bool, } -#[derive(Default, Debug, Clone)] +#[non_exhaustive] +#[derive(Default, Debug, Clone, Builder)] +#[builder(default)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] /// Options for parser functions. -pub struct ComrakParseOptions { +pub struct ParseOptions { /// Punctuation (quotes, full-stops and hyphens) are converted into 'smart' punctuation. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("'Hello,' \"world\" ...", &options), /// "<p>'Hello,' &quot;world&quot; ...</p>\n"); /// @@ -366,8 +377,8 @@ /// The default info string for fenced code blocks. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("```\nfn hello();\n```\n", &options), /// "<pre><code>fn hello();\n</code></pre>\n"); /// @@ -379,18 +390,35 @@ /// Whether or not a simple `x` or `X` is used for tasklist or any other symbol is allowed. pub relaxed_tasklist_matching: bool, + + /// Relax parsing of autolinks, allowing links to be detected inside brackets. + /// + /// ``` + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); + /// options.extension.autolink = true; + /// assert_eq!(markdown_to_html("[https://foo.com]", &options), + /// "<p>[https://foo.com]</p>\n"); + /// + /// options.parse.relaxed_autolinks = true; + /// assert_eq!(markdown_to_html("[https://foo.com]", &options), + /// "<p>[<a href=\"https://foo.com\">https://foo.com</a>]</p>\n"); + /// ``` + pub relaxed_autolinks: bool, } -#[derive(Default, Debug, Clone, Copy)] +#[non_exhaustive] +#[derive(Default, Debug, Clone, Copy, Builder)] +#[builder(default)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] /// Options for formatter functions. -pub struct ComrakRenderOptions { +pub struct RenderOptions { /// [Soft line breaks](http://spec.commonmark.org/0.27/#soft-line-breaks) in the input /// translate into hard line breaks in the output. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("Hello.\nWorld.\n", &options), /// "<p>Hello.\nWorld.</p>\n"); /// @@ -403,8 +431,8 @@ /// GitHub-style `<pre lang="xyz">` is used for fenced code blocks with info tags. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("``` rust\nfn hello();\n```\n", &options), /// "<pre><code class=\"language-rust\">fn hello();\n</code></pre>\n"); /// @@ -417,8 +445,8 @@ /// Enable full info strings for code blocks /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// assert_eq!(markdown_to_html("``` rust extra info\nfn hello();\n```\n", &options), /// "<pre><code class=\"language-rust\">fn hello();\n</code></pre>\n"); /// @@ -432,10 +460,10 @@ /// The wrap column when outputting CommonMark. /// /// ``` - /// # use comrak::{parse_document, ComrakOptions, format_commonmark}; + /// # use comrak::{parse_document, Options, format_commonmark}; /// # fn main() { /// # let arena = typed_arena::Arena::new(); - /// let mut options = ComrakOptions::default(); + /// let mut options = Options::default(); /// let node = parse_document(&arena, "hello hello hello hello hello hello", &options); /// let mut output = vec![]; /// format_commonmark(node, &options, &mut output).unwrap(); @@ -454,8 +482,8 @@ /// Allow rendering of raw HTML and potentially dangerous links. /// /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// let input = "<script>\nalert('xyz');\n</script>\n\n\ /// Possibly <marquee>annoying</marquee>.\n\n\ /// [Dangerous](javascript:alert(document.cookie)).\n\n\ @@ -478,8 +506,8 @@ /// Escape raw HTML instead of clobbering it. /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_html, Options}; + /// let mut options = Options::default(); /// let input = "<i>italic text</i>"; /// /// assert_eq!(markdown_to_html(input, &options), @@ -498,8 +526,8 @@ /// * `ListStyleType::Star` to use `*` /// /// ```rust - /// # use comrak::{markdown_to_commonmark, ComrakOptions, ListStyleType}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_commonmark, Options, ListStyleType}; + /// let mut options = Options::default(); /// let input = "- one\n- two\n- three"; /// assert_eq!(markdown_to_commonmark(input, &options), /// "- one\n- two\n- three\n"); // default is Dash @@ -519,8 +547,8 @@ /// Not yet compatible with extension.description_lists. /// /// ```rust - /// # use comrak::{markdown_to_commonmark_xml, ComrakOptions}; - /// let mut options = ComrakOptions::default(); + /// # use comrak::{markdown_to_commonmark_xml, Options}; + /// let mut options = Options::default(); /// options.render.sourcepos = true; /// let input = "Hello *world*!"; /// let xml = markdown_to_commonmark_xml(input, &options); @@ -529,25 +557,29 @@ pub sourcepos: bool, } -#[derive(Default, Debug)] +#[non_exhaustive] +#[derive(Default, Debug, Clone, Builder)] +#[builder(default)] /// Umbrella plugins struct. -pub struct ComrakPlugins<'p> { +pub struct Plugins<'p> { /// Configure render-time plugins. - pub render: ComrakRenderPlugins<'p>, + pub render: RenderPlugins<'p>, } -#[derive(Default)] +#[non_exhaustive] +#[derive(Default, Clone, Builder)] +#[builder(default)] /// Plugins for alternative rendering. -pub struct ComrakRenderPlugins<'p> { +pub struct RenderPlugins<'p> { /// Provide a syntax highlighter adapter implementation for syntax /// highlighting of codefence blocks. /// ``` - /// # use comrak::{markdown_to_html, ComrakOptions, ComrakPlugins, markdown_to_html_with_plugins}; + /// # use comrak::{markdown_to_html, Options, Plugins, markdown_to_html_with_plugins}; /// # use comrak::adapters::SyntaxHighlighterAdapter; /// use std::collections::HashMap; /// use std::io::{self, Write}; - /// let options = ComrakOptions::default(); - /// let mut plugins = ComrakPlugins::default(); + /// let options = Options::default(); + /// let mut plugins = Plugins::default(); /// let input = "```rust\nfn main<'a>();\n```"; /// /// assert_eq!(markdown_to_html_with_plugins(input, &options, &plugins), @@ -580,9 +612,9 @@ pub heading_adapter: Option<&'p dyn HeadingAdapter>, } -impl Debug for ComrakRenderPlugins<'_> { +impl Debug for RenderPlugins<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ComrakRenderPlugins") + f.debug_struct("RenderPlugins") .field( "codefence_syntax_highlighter", &"impl SyntaxHighlighterAdapter", @@ -600,13 +632,15 @@ struct FootnoteDefinition<'a> { ix: Option<u32>, node: &'a AstNode<'a>, + name: String, + total_references: u32, } impl<'a, 'o, 'c> Parser<'a, 'o, 'c> { fn new( arena: &'a Arena<AstNode<'a>>, root: &'a AstNode<'a>, - options: &'o ComrakOptions, + options: &'o Options, callback: Option<Callback<'c>>, ) -> Self { Parser { @@ -941,11 +975,13 @@ let mut nl: NodeList = NodeList::default(); let mut sc: scanners::SetextChar = scanners::SetextChar::Equals; let mut maybe_lazy = node_matches!(self.current, NodeValue::Paragraph); + let mut depth = 0; while !node_matches!( container, NodeValue::CodeBlock(..) | NodeValue::HtmlBlock(..) ) { + depth += 1; self.find_first_nonspace(line); let indented = self.indent >= CODE_INDENT; @@ -1069,6 +1105,7 @@ self.advance_offset(line, adv, false); } else if !indented && self.options.extension.footnotes + && depth < MAX_LIST_DEPTH && unwrap_into( scanners::footnote_definition(&line[self.first_nonspace..]), &mut matched, @@ -1080,7 +1117,10 @@ self.advance_offset(line, offset, false); *container = self.add_child( container, - NodeValue::FootnoteDefinition(str::from_utf8(c).unwrap().to_string()), + NodeValue::FootnoteDefinition(NodeFootnoteDefinition { + name: str::from_utf8(c).unwrap().to_string(), + total_references: 0, + }), self.first_nonspace + 1, ); container.data.borrow_mut().internal_offset = matched; @@ -1096,6 +1136,7 @@ } } else if (!indented || node_matches!(container, NodeValue::List(..))) && self.indent < 4 + && depth < MAX_LIST_DEPTH && unwrap_into_2( parse_list_marker( line, @@ -1754,14 +1795,22 @@ let mut ix = 0; Self::find_footnote_references(self.root, &mut map, &mut ix); + if !map.is_empty() { + // In order for references to be found inside footnote definitions, + // such as `[^1]: another reference[^2]`, + // the node needed to remain in the AST. Now we can remove them. + Self::cleanup_footnote_definitions(self.root); + } + if ix > 0 { let mut v = map.into_values().collect::<Vec<_>>(); v.sort_unstable_by(|a, b| a.ix.cmp(&b.ix)); for f in v { - if let Some(ix) = f.ix { + if f.ix.is_some() { match f.node.data.borrow_mut().value { - NodeValue::FootnoteDefinition(ref mut name) => { - *name = format!("{}", ix); + NodeValue::FootnoteDefinition(ref mut nfd) => { + nfd.name = f.name.to_string(); + nfd.total_references = f.total_references; } _ => unreachable!(), } @@ -1776,11 +1825,15 @@ map: &mut HashMap<String, FootnoteDefinition<'a>>, ) { match node.data.borrow().value { - NodeValue::FootnoteDefinition(ref name) => { - node.detach(); + NodeValue::FootnoteDefinition(ref nfd) => { map.insert( - strings::normalize_label(name), - FootnoteDefinition { ix: None, node }, + strings::normalize_label(&nfd.name, Case::DontPreserve), + FootnoteDefinition { + ix: None, + node, + name: strings::normalize_label(&nfd.name, Case::Preserve), + total_references: 0, + }, ); } _ => { @@ -1799,8 +1852,9 @@ let mut ast = node.data.borrow_mut(); let mut replace = None; match ast.value { - NodeValue::FootnoteReference(ref mut name) => { - if let Some(ref mut footnote) = map.get_mut(name) { + NodeValue::FootnoteReference(ref mut nfr) => { + let normalized = strings::normalize_label(&nfr.name, Case::DontPreserve); + if let Some(ref mut footnote) = map.get_mut(&normalized) { let ix = match footnote.ix { Some(ix) => ix, None => { @@ -1809,9 +1863,12 @@ *ixp } }; - *name = format!("{}", ix); + footnote.total_references += 1; + nfr.ref_num = footnote.total_references; + nfr.ix = ix; + nfr.name = strings::normalize_label(&footnote.name, Case::Preserve); } else { - replace = Some(name.clone()); + replace = Some(nfr.name.clone()); } } _ => { @@ -1828,6 +1885,19 @@ } } + fn cleanup_footnote_definitions(node: &'a AstNode<'a>) { + match node.data.borrow().value { + NodeValue::FootnoteDefinition(_) => { + node.detach(); + } + _ => { + for n in node.children() { + Self::cleanup_footnote_definitions(n); + } + } + } + } + fn postprocess_text_nodes(&mut self, node: &'a AstNode<'a>) { let mut stack = vec![node]; let mut children = vec![]; @@ -1900,7 +1970,12 @@ } if self.options.extension.autolink { - autolink::process_autolinks(self.arena, node, text); + autolink::process_autolinks( + self.arena, + node, + text, + self.options.parse.relaxed_autolinks, + ); } } @@ -2010,7 +2085,7 @@ } } - lab = strings::normalize_label(&lab); + lab = strings::normalize_label(&lab, Case::DontPreserve); if !lab.is_empty() { subj.refmap.map.entry(lab).or_insert(Reference { url: String::from_utf8(strings::clean_url(url)).unwrap(), @@ -2178,7 +2253,7 @@ #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -/// Options for bulleted list redering in markdown. See `link_style` in [ComrakRenderOptions] for more details. +/// Options for bulleted list redering in markdown. See `link_style` in [RenderOptions] for more details. pub enum ListStyleType { /// The `-` character Dash = 45, diff -Nru rust-comrak-0.18.0/src/parser/shortcodes.rs rust-comrak-0.20.0/src/parser/shortcodes.rs --- rust-comrak-0.18.0/src/parser/shortcodes.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/parser/shortcodes.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,7 +1,7 @@ use std::{convert::TryFrom, str}; /// The details of an inline emoji. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeShortCode( /// A short code that is translated into an emoji String, diff -Nru rust-comrak-0.18.0/src/parser/table.rs rust-comrak-0.20.0/src/parser/table.rs --- rust-comrak-0.18.0/src/parser/table.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/parser/table.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,6 +1,6 @@ use crate::arena_tree::Node; use crate::nodes; -use crate::nodes::{Ast, AstNode, NodeValue, TableAlignment}; +use crate::nodes::{Ast, AstNode, NodeTable, NodeValue, TableAlignment}; use crate::parser::Parser; use crate::scanners; use crate::strings::trim; @@ -9,6 +9,9 @@ use super::inlines::count_newlines; +// Limit to prevent a malicious input from causing a denial of service. +const MAX_AUTOCOMPLETED_CELLS: usize = 500_000; + pub fn try_opening_block<'a>( parser: &mut Parser<'a, '_, '_>, container: &'a AstNode<'a>, @@ -16,7 +19,7 @@ ) -> Option<(&'a AstNode<'a>, bool, bool)> { let aligns = match container.data.borrow().value { NodeValue::Paragraph => None, - NodeValue::Table(ref aligns) => Some(aligns.clone()), + NodeValue::Table(NodeTable { ref alignments, .. }) => Some(alignments.clone()), _ => return None, }; @@ -39,14 +42,17 @@ return Some((container, false, false)); } - let marker_row = row(&line[parser.first_nonspace..]).unwrap(); + let delimiter_row = match row(&line[parser.first_nonspace..]) { + Some(delimiter_row) => delimiter_row, + None => return Some((container, false, true)), + }; let header_row = match row(container.data.borrow().content.as_bytes()) { Some(header_row) => header_row, None => return Some((container, false, true)), }; - if header_row.cells.len() != marker_row.cells.len() { + if header_row.cells.len() != delimiter_row.cells.len() { return Some((container, false, true)); } @@ -55,7 +61,7 @@ } let mut alignments = vec![]; - for cell in marker_row.cells { + for cell in delimiter_row.cells { let cell_content = cell.content.as_bytes(); let left = !cell_content.is_empty() && cell_content[0] == b':'; let right = !cell_content.is_empty() && cell_content[cell_content.len() - 1] == b':'; @@ -71,7 +77,15 @@ } let start = container.data.borrow().sourcepos.start; - let child = Ast::new(NodeValue::Table(alignments), start); + let child = Ast::new( + NodeValue::Table(NodeTable { + alignments, + num_columns: header_row.cells.len(), + num_rows: 0, + num_nonempty_cells: 0, + }), + start, + ); let table = parser.arena.alloc(Node::new(RefCell::new(child))); container.append(table); @@ -85,7 +99,10 @@ ); } - for cell in header_row.cells { + let mut i = 0; + + while i < header_row.cells.len() { + let cell = &header_row.cells[i]; let ast_cell = parser.add_child( header, NodeValue::TableCell, @@ -97,8 +114,12 @@ start.column_add((cell.end_offset - header_row.paragraph_offset) as isize); ast.internal_offset = cell.internal_offset; ast.content = cell.content.clone(); + + i += 1; } + incr_table_row_count(container, i); + let offset = line.len() - 1 - parser.offset; parser.advance_offset(line, offset, false); @@ -114,8 +135,17 @@ if parser.blank { return None; } + + if get_num_autocompleted_cells(container) > MAX_AUTOCOMPLETED_CELLS { + return None; + } + let sourcepos = container.data.borrow().sourcepos; - let this_row = row(&line[parser.first_nonspace..]).unwrap(); + let this_row = match row(&line[parser.first_nonspace..]) { + Some(this_row) => this_row, + None => return None, + }; + let new_row = parser.add_child( container, NodeValue::TableRow(false), @@ -141,9 +171,12 @@ cell_ast.content = cell.content.clone(); last_column = cell_ast.sourcepos.end.column; + i += 1; } + incr_table_row_count(container, i); + while i < alignments.len() { parser.add_child(new_row, NodeValue::TableCell, last_column); i += 1; @@ -175,6 +208,7 @@ let mut paragraph_offset = 0; let mut expect_more_cells = true; + let mut max_columns_abort = false; while offset < len && expect_more_cells { let cell_matched = scanners::table_cell(&string[offset..]).unwrap_or(0); @@ -192,6 +226,12 @@ internal_offset += 1; } + // set an upper limit on the number of columns + if cells.len() == <u16 as Into<usize>>::into(u16::MAX) { + max_columns_abort = true; + break; + } + cells.push(Cell { start_offset, end_offset: offset + cell_matched - 1, @@ -219,7 +259,7 @@ } } - if offset != len || cells.is_empty() { + if offset != len || cells.is_empty() || max_columns_abort { None } else { Some(Row { @@ -291,6 +331,40 @@ v } +// Increment the number of rows in the table. Also update n_nonempty_cells, +// which keeps track of the number of cells which were parsed from the +// input file. (If one of the rows is too short, then the trailing cells +// are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.) +// The purpose of this is to prevent a malicious input from generating a very +// large number of autocompleted cells, which could cause a denial of service +// vulnerability. +fn incr_table_row_count<'a>(container: &'a AstNode<'a>, i: usize) -> bool { + return match container.data.borrow_mut().value { + NodeValue::Table(ref mut node_table) => { + node_table.num_rows += 1; + node_table.num_nonempty_cells += i; + true + } + _ => false, + }; +} + +// Calculate the number of autocompleted cells. +fn get_num_autocompleted_cells<'a>(container: &'a AstNode<'a>) -> usize { + return match container.data.borrow().value { + NodeValue::Table(ref node_table) => { + let num_cells = node_table.num_columns * node_table.num_rows; + + if num_cells < node_table.num_nonempty_cells { + 0 + } else { + (node_table.num_columns * node_table.num_rows) - node_table.num_nonempty_cells + } + } + _ => 0, + }; +} + pub fn matches(line: &[u8]) -> bool { row(line).is_some() } diff -Nru rust-comrak-0.18.0/src/plugins/mod.rs rust-comrak-0.20.0/src/plugins/mod.rs --- rust-comrak-0.18.0/src/plugins/mod.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/plugins/mod.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,4 +1,5 @@ //! Plugins for enhancing the default implementation of comrak can be defined in this module. #[cfg(feature = "syntect")] +#[cfg_attr(docsrs, doc(cfg(feature = "syntect")))] pub mod syntect; diff -Nru rust-comrak-0.18.0/src/plugins/syntect.rs rust-comrak-0.20.0/src/plugins/syntect.rs --- rust-comrak-0.18.0/src/plugins/syntect.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/plugins/syntect.rs 2006-07-24 01:21:28.000000000 +0000 @@ -6,7 +6,9 @@ use std::io::{self, Write}; use syntect::easy::HighlightLines; use syntect::highlighting::{Color, ThemeSet}; -use syntect::html::{append_highlighted_html_for_styled_line, IncludeBackground}; +use syntect::html::{ + append_highlighted_html_for_styled_line, ClassStyle, ClassedHTMLGenerator, IncludeBackground, +}; use syntect::parsing::{SyntaxReference, SyntaxSet}; use syntect::util::LinesWithEndings; use syntect::Error; @@ -14,37 +16,55 @@ #[derive(Debug)] /// Syntect syntax highlighter plugin. pub struct SyntectAdapter { - theme: String, + theme: Option<String>, syntax_set: SyntaxSet, theme_set: ThemeSet, } impl SyntectAdapter { /// Construct a new `SyntectAdapter` object and set the syntax highlighting theme. - pub fn new(theme: &str) -> Self { + /// If None is specified, apply CSS classes instead. + pub fn new(theme: Option<&str>) -> Self { SyntectAdapter { - theme: theme.into(), + theme: theme.map(String::from), syntax_set: SyntaxSet::load_defaults_newlines(), theme_set: ThemeSet::load_defaults(), } } fn highlight_html(&self, code: &str, syntax: &SyntaxReference) -> Result<String, Error> { - // syntect::html::highlighted_html_for_string, without the opening/closing <pre>. - let theme = &self.theme_set.themes[&self.theme]; - let mut highlighter = HighlightLines::new(syntax, theme); - let mut output = String::new(); - let bg = theme.settings.background.unwrap_or(Color::WHITE); - - for line in LinesWithEndings::from(code) { - let regions = highlighter.highlight_line(line, &self.syntax_set)?; - append_highlighted_html_for_styled_line( - &regions[..], - IncludeBackground::IfDifferent(bg), - &mut output, - )?; + match &self.theme { + Some(theme) => { + // syntect::html::highlighted_html_for_string, without the opening/closing <pre>. + let theme = &self.theme_set.themes[theme]; + let mut highlighter = HighlightLines::new(syntax, theme); + + let bg = theme.settings.background.unwrap_or(Color::WHITE); + + let mut output = String::new(); + for line in LinesWithEndings::from(code) { + let regions = highlighter.highlight_line(line, &self.syntax_set)?; + append_highlighted_html_for_styled_line( + &regions[..], + IncludeBackground::IfDifferent(bg), + &mut output, + )?; + } + Ok(output) + } + None => { + // fall back to HTML classes. + let mut html_generator = ClassedHTMLGenerator::new_with_class_style( + syntax, + &self.syntax_set, + ClassStyle::Spaced, + ); + for line in LinesWithEndings::from(code) { + html_generator.parse_html_for_line_which_includes_newline(line)?; + } + Ok(html_generator.finalize()) + } } - Ok(output) } } @@ -82,16 +102,25 @@ output: &mut dyn Write, attributes: HashMap<String, String>, ) -> io::Result<()> { - let theme = &self.theme_set.themes[&self.theme]; - let colour = theme.settings.background.unwrap_or(Color::WHITE); - - let style = format!( - "background-color:#{:02x}{:02x}{:02x};", - colour.r, colour.g, colour.b - ); + match &self.theme { + Some(theme) => { + let theme = &self.theme_set.themes[theme]; + let colour = theme.settings.background.unwrap_or(Color::WHITE); + + let style = format!( + "background-color:#{:02x}{:02x}{:02x};", + colour.r, colour.g, colour.b + ); - let mut pre_attributes = SyntectPreAttributes::new(attributes, &style); - html::write_opening_tag(output, "pre", pre_attributes.iter_mut()) + let mut pre_attributes = SyntectPreAttributes::new(attributes, &style); + html::write_opening_tag(output, "pre", pre_attributes.iter_mut()) + } + None => { + let mut attributes: HashMap<&str, &str> = HashMap::new(); + attributes.insert("class", "syntax-highlighting"); + html::write_opening_tag(output, "pre", attributes) + } + } } fn write_code_tag( @@ -151,7 +180,7 @@ } } -#[derive(Debug, Default)] +#[derive(Debug)] /// A builder for [`SyntectAdapter`]. /// /// Allows customization of `Theme`, [`ThemeSet`], and [`SyntaxSet`]. @@ -161,25 +190,41 @@ theme_set: Option<ThemeSet>, } +impl Default for SyntectAdapterBuilder { + fn default() -> Self { + SyntectAdapterBuilder { + theme: Some("InspiredGitHub".into()), + syntax_set: None, + theme_set: None, + } + } +} + impl SyntectAdapterBuilder { - /// Creates a new empty [`SyntectAdapterBuilder`] + /// Create a new empty [`SyntectAdapterBuilder`]. pub fn new() -> Self { Default::default() } - /// Sets the theme + /// Set the theme. pub fn theme(mut self, s: &str) -> Self { self.theme.replace(s.into()); self } - /// Sets the syntax set + /// Uses CSS classes instead of a Syntect theme. + pub fn css(mut self) -> Self { + self.theme = None; + self + } + + /// Set the syntax set. pub fn syntax_set(mut self, s: SyntaxSet) -> Self { self.syntax_set.replace(s); self } - /// Sets the theme set + /// Set the theme set. pub fn theme_set(mut self, s: ThemeSet) -> Self { self.theme_set.replace(s); self @@ -191,7 +236,7 @@ /// - `theme_set`: [`ThemeSet::load_defaults()`] pub fn build(self) -> SyntectAdapter { SyntectAdapter { - theme: self.theme.unwrap_or_else(|| "InspiredGitHub".into()), + theme: self.theme, syntax_set: self .syntax_set .unwrap_or_else(SyntaxSet::load_defaults_newlines), diff -Nru rust-comrak-0.18.0/src/scanners.re rust-comrak-0.20.0/src/scanners.re --- rust-comrak-0.18.0/src/scanners.re 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/scanners.re 2006-07-24 01:21:28.000000000 +0000 @@ -316,7 +316,7 @@ table_spacechar = [ \t\v\f]; table_newline = [\r]?[\n]; - table_marker = (table_spacechar*[:]?[-]+[:]?table_spacechar*); + table_delimiter = (table_spacechar*[:]?[-]+[:]?table_spacechar*); table_cell = (escaped_char|[^\x00|\r\n])+; */ @@ -326,7 +326,7 @@ let mut marker = 0; let len = s.len(); /*!re2c - [|]? table_marker ([|] table_marker)* [|]? table_spacechar* table_newline { + [|]? table_delimiter ([|] table_delimiter)* [|]? table_spacechar* table_newline { return Some(cursor); } * { return None; } diff -Nru rust-comrak-0.18.0/src/strings.rs rust-comrak-0.20.0/src/strings.rs --- rust-comrak-0.18.0/src/strings.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/strings.rs 2006-07-24 01:21:28.000000000 +0000 @@ -4,6 +4,12 @@ use std::ptr; use std::str; +#[derive(PartialEq, Eq)] +pub enum Case { + Preserve, + DontPreserve, +} + pub fn unescape(v: &mut Vec<u8>) { let mut r = 0; let mut prev = None; @@ -11,6 +17,10 @@ while r < v.len() { if v[r] == b'\\' && r + 1 < v.len() && ispunct(v[r + 1]) { + if v[r + 1] == b'\\' { + r += 1; + } + if let Some(prev) = prev { let window = &mut v[(prev + 1 - found)..r]; shift_buf_left(window, found); @@ -237,7 +247,7 @@ true } -pub fn normalize_label(i: &str) -> String { +pub fn normalize_label(i: &str, casing: Case) -> String { // trim_slice only removes bytes from start and end that match isspace(); // result is UTF-8. let i = unsafe { str::from_utf8_unchecked(trim_slice(i.as_bytes())) }; @@ -245,15 +255,16 @@ let mut v = String::with_capacity(i.len()); let mut last_was_whitespace = false; for c in i.chars() { - for e in c.to_lowercase() { - if e.is_whitespace() { - if !last_was_whitespace { - last_was_whitespace = true; - v.push(' '); - } - } else { - last_was_whitespace = false; - v.push(e); + if c.is_whitespace() { + if !last_was_whitespace { + last_was_whitespace = true; + v.push(' '); + } + } else { + last_was_whitespace = false; + match casing { + Case::Preserve => v.push(c), + Case::DontPreserve => v.push_str(&c.to_lowercase().to_string()), } } } @@ -308,7 +319,8 @@ #[cfg(test)] pub mod tests { - use super::{normalize_code, split_off_front_matter}; + use super::{normalize_code, normalize_label, split_off_front_matter}; + use crate::strings::Case; #[test] fn normalize_code_handles_lone_newline() { @@ -341,4 +353,25 @@ Some(("!@#\r\n\r\nfoo: \n!@# \r\nquux\n!@#\r\n\n", "\nYes!\n")) ); } + + #[test] + fn normalize_label_lowercase() { + assert_eq!( + normalize_label(" Foo\u{A0}BAR ", Case::DontPreserve), + "foo bar" + ); + assert_eq!( + normalize_label(" FooİBAR ", Case::DontPreserve), + "fooi\u{307}bar" + ); + } + + #[test] + fn normalize_label_preserve() { + assert_eq!( + normalize_label(" Foo\u{A0}BAR ", Case::Preserve), + "Foo BAR" + ); + assert_eq!(normalize_label(" FooİBAR ", Case::Preserve), "FooİBAR"); + } } diff -Nru rust-comrak-0.18.0/src/tests/api.rs rust-comrak-0.20.0/src/tests/api.rs --- rust-comrak-0.18.0/src/tests/api.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/api.rs 2006-07-24 01:21:28.000000000 +0000 @@ -8,8 +8,8 @@ #[test] fn exercise_full_api() { let arena = Arena::new(); - let default_options = ComrakOptions::default(); - let default_plugins = ComrakPlugins::default(); + let default_options = Options::default(); + let default_plugins = Plugins::default(); let node = parse_document(&arena, "# My document\n", &default_options); let mut buffer = vec![]; @@ -34,37 +34,35 @@ Some(&mut |_: &str| Some(("abc".to_string(), "xyz".to_string()))), ); - let _ = ComrakOptions { - extension: ComrakExtensionOptions { - strikethrough: false, - tagfilter: false, - table: false, - autolink: false, - tasklist: false, - superscript: false, - header_ids: Some("abc".to_string()), - footnotes: false, - description_lists: false, - front_matter_delimiter: None, - #[cfg(feature = "shortcodes")] - shortcodes: true, - }, - parse: ComrakParseOptions { - smart: false, - default_info_string: Some("abc".to_string()), - relaxed_tasklist_matching: true, - }, - render: ComrakRenderOptions { - hardbreaks: false, - github_pre_lang: false, - full_info_string: false, - width: 123456, - unsafe_: false, - escape: false, - list_style: ListStyleType::Dash, - sourcepos: false, - }, - }; + let mut extension = ExtensionOptionsBuilder::default(); + extension.strikethrough(false); + extension.tagfilter(false); + extension.table(false); + extension.autolink(false); + extension.tasklist(false); + extension.superscript(false); + extension.header_ids(Some("abc".to_string())); + extension.footnotes(false); + extension.description_lists(false); + extension.front_matter_delimiter(None); + #[cfg(feature = "shortcodes")] + extension.shortcodes(true); + + let mut parse = ParseOptionsBuilder::default(); + parse.smart(false); + parse.default_info_string(Some("abc".to_string())); + parse.relaxed_tasklist_matching(false); + parse.relaxed_autolinks(false); + + let mut render = RenderOptionsBuilder::default(); + render.hardbreaks(false); + render.github_pre_lang(false); + render.full_info_string(false); + render.width(123456); + render.unsafe_(false); + render.escape(false); + render.list_style(ListStyleType::Dash); + render.sourcepos(false); pub struct MockAdapter {} impl SyntaxHighlighterAdapter for MockAdapter { @@ -111,12 +109,12 @@ let mock_adapter = MockAdapter {}; - let _ = ComrakPlugins { - render: ComrakRenderPlugins { - codefence_syntax_highlighter: Some(&mock_adapter), - heading_adapter: Some(&mock_adapter), - }, - }; + let mut render_plugins = RenderPluginsBuilder::default(); + render_plugins.codefence_syntax_highlighter(Some(&mock_adapter)); + render_plugins.heading_adapter(Some(&mock_adapter)); + + let mut plugins = PluginsBuilder::default(); + plugins.render(render_plugins.build().unwrap()); let _: String = markdown_to_html("# Yes", &default_options); @@ -164,12 +162,15 @@ let _: bool = nh.setext; } nodes::NodeValue::ThematicBreak => {} - nodes::NodeValue::FootnoteDefinition(name) => { - let _: &String = name; - } - nodes::NodeValue::Table(aligns) => { - let _: &Vec<nodes::TableAlignment> = aligns; - match aligns[0] { + nodes::NodeValue::FootnoteDefinition(nfd) => { + let _: &String = &nfd.name; + let _: u32 = nfd.total_references; + } + nodes::NodeValue::Table(nt) => { + let _: &Vec<nodes::TableAlignment> = &nt.alignments; + let _: usize = nt.num_nonempty_cells; + let _: usize = nt.num_rows; + match nt.alignments[0] { nodes::TableAlignment::None => {} nodes::TableAlignment::Left => {} nodes::TableAlignment::Center => {} @@ -207,8 +208,9 @@ nodes::NodeValue::ShortCode(ne) => { let _: &str = ne.shortcode(); } - nodes::NodeValue::FootnoteReference(name) => { - let _: &String = name; + nodes::NodeValue::FootnoteReference(nfr) => { + let _: String = nfr.name; + let _: u32 = nfr.ix; } } } diff -Nru rust-comrak-0.18.0/src/tests/autolink.rs rust-comrak-0.20.0/src/tests/autolink.rs --- rust-comrak-0.18.0/src/tests/autolink.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/autolink.rs 2006-07-24 01:21:28.000000000 +0000 @@ -53,6 +53,56 @@ } #[test] +fn autolink_ignore_links_in_brackets() { + let examples = [ + ["[https://foo.com]", "<p>[https://foo.com]</p>\n"], + ["[[https://foo.com]]", "<p>[[https://foo.com]]</p>\n"], + [ + "[[Foo|https://foo.com]]", + "<p>[[Foo|https://foo.com]]</p>\n", + ], + [ + "[<https://foo.com>]", + "<p>[<a href=\"https://foo.com\">https://foo.com</a>]</p>\n", + ], + ]; + + for example in examples { + html_opts!([extension.autolink], example[0], example[1]); + } +} + +#[test] +fn autolink_relaxed_links_in_brackets() { + let examples = [ + [ + "[https://foo.com]", + "<p>[<a href=\"https://foo.com\">https://foo.com</a>]</p>\n", + ], + [ + "[[https://foo.com]]", + "<p>[[<a href=\"https://foo.com\">https://foo.com</a>]]</p>\n", + ], + [ + "[[Foo|https://foo.com]]", + "<p>[[Foo|<a href=\"https://foo.com\">https://foo.com</a>]]</p>\n", + ], + [ + "[<https://foo.com>]", + "<p>[<a href=\"https://foo.com\">https://foo.com</a>]</p>\n", + ], + ]; + + for example in examples { + html_opts!( + [extension.autolink, parse.relaxed_autolinks], + example[0], + example[1] + ); + } +} + +#[test] fn sourcepos_correctly_restores_context() { // There's unsoundness in trying to maintain and adjust sourcepos // when doing autolinks in the light of: diff -Nru rust-comrak-0.18.0/src/tests/commonmark.rs rust-comrak-0.20.0/src/tests/commonmark.rs --- rust-comrak-0.18.0/src/tests/commonmark.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/commonmark.rs 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,11 @@ +use super::*; + +#[test] +fn commonmark_removes_redundant_strong() { + let options = ComrakOptions::default(); + + let input = "This is **something **even** better**"; + let output = "This is **something even better**\n"; + + commonmark(input, output, Some(&options)); +} diff -Nru rust-comrak-0.18.0/src/tests/core.rs rust-comrak-0.20.0/src/tests/core.rs --- rust-comrak-0.18.0/src/tests/core.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/core.rs 2006-07-24 01:21:28.000000000 +0000 @@ -239,7 +239,7 @@ let input = "Some `code1`. More ``` code2 ```.\n"; let arena = Arena::new(); - let options = ComrakOptions::default(); + let options = Options::default(); let root = parse_document(&arena, input, &options); let code1 = NodeValue::Code(NodeCode { @@ -306,6 +306,38 @@ "<p><a href=\"/here\">Where am I?</a></p>\n" ), ); + html( + concat!( + r"Where are you [going](#1\.-link (today))?", + "\n", + "\n", + "[Where am I?](/here)\n" + ), + concat!( + "<p>Where are you <a href=\"#1.-link\" \ + title=\"today\">going</a>?</p>\n", + "<p><a href=\"/here\">Where am I?</a></p>\n" + ), + ); + html( + r"[Link Text](\\\\)", + concat!(r##"<p><a href="%5C%5C">Link Text</a></p>"##, '\n'), + ); + html( + r"[Link Text](\\\\\\\\\\)", + concat!(r##"<p><a href="%5C%5C%5C%5C%5C">Link Text</a></p>"##, '\n'), + ); + html( + r"[Link Text](\\\\ (title))", + concat!( + r##"<p><a href="%5C%5C" title="title">Link Text</a></p>"##, + '\n' + ), + ); + html( + r"[Link Text](\#)", + concat!(r##"<p><a href="#">Link Text</a></p>"##, '\n'), + ); } #[test] diff -Nru rust-comrak-0.18.0/src/tests/footnotes.rs rust-comrak-0.20.0/src/tests/footnotes.rs --- rust-comrak-0.18.0/src/tests/footnotes.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/footnotes.rs 2006-07-24 01:21:28.000000000 +0000 @@ -7,7 +7,7 @@ concat!( "Here is a[^nowhere] footnote reference,[^1] and another.[^longnote]\n", "\n", - "This is another note.[^note]\n", + "This is another note.[^note] And footnote[^longnote] is referenced again.\n", "\n", "[^note]: Hi.\n", "\n", @@ -26,26 +26,28 @@ concat!( "<p>Here is a[^nowhere] footnote reference,<sup class=\"footnote-ref\"><a href=\"#fn-1\" \ id=\"fnref-1\" data-footnote-ref>1</a></sup> and another.<sup class=\"footnote-ref\"><a \ - href=\"#fn-2\" id=\"fnref-2\" data-footnote-ref>2</a></sup></p>\n", - "<p>This is another note.<sup class=\"footnote-ref\"><a href=\"#fn-3\" \ - id=\"fnref-3\" data-footnote-ref>3</a></sup></p>\n", + href=\"#fn-longnote\" id=\"fnref-longnote\" data-footnote-ref>2</a></sup></p>\n", + "<p>This is another note.<sup class=\"footnote-ref\"><a \ + href=\"#fn-note\" id=\"fnref-note\" data-footnote-ref>3</a></sup> And footnote<sup class=\"footnote-ref\"><a \ + href=\"#fn-longnote\" id=\"fnref-longnote-2\" data-footnote-ref>2</a></sup> is referenced again.</p>\n", "<p>This is regular content.</p>\n", "<section class=\"footnotes\" data-footnotes>\n", "<ol>\n", "<li id=\"fn-1\">\n", "<p>Here is the footnote. <a href=\"#fnref-1\" \ - class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", "</li>\n", - "<li id=\"fn-2\">\n", + "<li id=\"fn-longnote\">\n", "<p>Here's one with multiple blocks.</p>\n", "<p>Subsequent paragraphs are indented.</p>\n", "<pre><code>code\n", "</code></pre>\n", - "<a href=\"#fnref-2\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a>\n", + "<a href=\"#fnref-longnote\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"2\" aria-label=\"Back to reference 2\">↩</a> \ + <a href=\"#fnref-longnote-2\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"2-2\" aria-label=\"Back to reference 2-2\">↩<sup class=\"footnote-ref\">2</sup></a>\n", "</li>\n", - "<li id=\"fn-3\">\n", - "<p>Hi. <a href=\"#fnref-3\" \ - class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + "<li id=\"fn-note\">\n", + "<p>Hi. <a href=\"#fnref-note\" \ + class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"3\" aria-label=\"Back to reference 3\">↩</a></p>\n", "</li>\n", "</ol>\n", "</section>\n" @@ -59,12 +61,11 @@ [extension.footnotes], concat!("Here's my footnote![^a]\n", "\n", "[^a]: Yep.\n"), concat!( - "<p>Here's my footnote!<sup class=\"footnote-ref\"><a href=\"#fn-1\" \ - id=\"fnref-1\" data-footnote-ref>1</a></sup></p>\n", + "<p>Here's my footnote!<sup class=\"footnote-ref\"><a href=\"#fn-a\" id=\"fnref-a\" data-footnote-ref>1</a></sup></p>\n", "<section class=\"footnotes\" data-footnotes>\n", "<ol>\n", - "<li id=\"fn-1\">\n", - "<p>Yep. <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + "<li id=\"fn-a\">\n", + "<p>Yep. <a href=\"#fnref-a\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", "</li>\n", "</ol>\n", "</section>\n" @@ -95,7 +96,7 @@ "</thead>\n", "<tbody>\n", "<tr>\n", - "<td>foot <sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\" data-footnote-ref>1</a></sup></td>\n", + "<td>foot <sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1-2\" data-footnote-ref>1</a></sup></td>\n", "<td>note</td>\n", "</tr>\n", "</tbody>\n", @@ -103,7 +104,7 @@ "<section class=\"footnotes\" data-footnotes>\n", "<ol>\n", "<li id=\"fn-1\">\n", - "<p>a footnote <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + "<p>a footnote <a href=\"#fnref-1\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a> <a href=\"#fnref-1-2\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1-2\" aria-label=\"Back to reference 1-2\">↩<sup class=\"footnote-ref\">2</sup></a></p>\n", "</li>\n", "</ol>\n", "</section>\n", @@ -127,18 +128,120 @@ concat!( "<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-1\" \ id=\"fnref-1\" data-footnote-ref>1</a></sup></p>\n", - "<p>Here is a longer footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-2\" \ - id=\"fnref-2\" data-footnote-ref>2</a></sup></p>\n", + "<p>Here is a longer footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-ref\" \ + id=\"fnref-ref\" data-footnote-ref>2</a></sup></p>\n", "<p>e = mc<sup>2</sup>.</p>\n", "<section class=\"footnotes\" data-footnotes>\n", "<ol>\n", "<li id=\"fn-1\">\n", "<p>Here is the footnote. <a href=\"#fnref-1\" \ - class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", + "</li>\n", + "<li id=\"fn-ref\">\n", + "<p>Here is another footnote. <a href=\"#fnref-ref\" \ + class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"2\" aria-label=\"Back to reference 2\">↩</a></p>\n", + "</li>\n", + "</ol>\n", + "</section>\n" + ), + ); +} + +#[test] +fn footnote_escapes_name() { + html_opts!( + [extension.footnotes], + concat!( + "Here is a footnote reference.[^😄ref]\n", + "\n", + "[^😄ref]: Here is the footnote.\n", + ), + concat!( + "<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-%F0%9F%98%84ref\" id=\"fnref-%F0%9F%98%84ref\" data-footnote-ref>1</a></sup></p>\n", + "<section class=\"footnotes\" data-footnotes>\n", + "<ol>\n", + "<li id=\"fn-%F0%9F%98%84ref\">\n", + "<p>Here is the footnote. <a href=\"#fnref-%F0%9F%98%84ref\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", "</li>\n", - "<li id=\"fn-2\">\n", - "<p>Here is another footnote. <a href=\"#fnref-2\" \ - class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a></p>\n", + "</ol>\n", + "</section>\n" + ), + ); +} + +#[test] +fn footnote_case_insensitive_and_case_preserving() { + html_opts!( + [extension.footnotes], + concat!( + "Here is a footnote reference.[^AB] and [^ab]\n", + "\n", + "[^aB]: Here is the footnote.\n", + ), + concat!( + "<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-aB\" id=\"fnref-aB\" data-footnote-ref>1</a></sup> and <sup class=\"footnote-ref\"><a href=\"#fn-aB\" id=\"fnref-aB-2\" data-footnote-ref>1</a></sup></p>\n", + "<section class=\"footnotes\" data-footnotes>\n", + "<ol>\n", + "<li id=\"fn-aB\">\n", + "<p>Here is the footnote. <a href=\"#fnref-aB\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a> <a href=\"#fnref-aB-2\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1-2\" aria-label=\"Back to reference 1-2\">↩<sup class=\"footnote-ref\">2</sup></a></p>\n", + "</li>\n", + "</ol>\n", + "</section>\n" + ), + ); +} + +#[test] +fn footnote_name_parsed_into_multiple_nodes() { + html_opts!( + [extension.footnotes], + concat!( + "Foo.[^_ab]\n", + "\n", + "[^_ab]: Here is the footnote.\n", + ), + concat!( + "<p>Foo.<sup class=\"footnote-ref\"><a href=\"#fn-_ab\" id=\"fnref-_ab\" data-footnote-ref>1</a></sup></p>\n", + "<section class=\"footnotes\" data-footnotes>\n", + "<ol>\n", + "<li id=\"fn-_ab\">\n", + "<p>Here is the footnote. <a href=\"#fnref-_ab\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", + "</li>\n", + "</ol>\n", + "</section>\n" + ), + ); +} + +#[test] +fn footnote_invalid_with_missing_name() { + html_opts!( + [extension.footnotes], + "Foo.[^]\n\n[^]: Here is the footnote.\n", + "<p>Foo.[^]</p>\n<p>[^]: Here is the footnote.</p>\n" + ); +} + +#[test] +fn footnote_does_not_allow_spaces_in_name() { + html_opts!( + [extension.footnotes], + "Foo.[^one two]\n\n[^one two]: Here is the footnote.\n", + "<p>Foo.[^one two]</p>\n<p>[^one two]: Here is the footnote.</p>\n" + ); +} + +#[test] +fn footnote_does_not_expand_emphasis_in_name() { + html_opts!( + [extension.footnotes], + "Foo[^**one**]\n[^**one**]: bar\n", + concat!( + "<p>Foo<sup class=\"footnote-ref\"><a href=\"#fn-**one**\" id=\"fnref-**one**\" data-footnote-ref>1</a></sup></p>\n", + "<section class=\"footnotes\" data-footnotes>\n", + "<ol>\n", + "<li id=\"fn-**one**\">\n", + "<p>bar <a href=\"#fnref-**one**\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n", "</li>\n", "</ol>\n", "</section>\n" diff -Nru rust-comrak-0.18.0/src/tests/fuzz.rs rust-comrak-0.20.0/src/tests/fuzz.rs --- rust-comrak-0.18.0/src/tests/fuzz.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/fuzz.rs 2006-07-24 01:21:28.000000000 +0000 @@ -10,7 +10,7 @@ html_opts!( [extension.tasklist, parse.relaxed_tasklist_matching], "* [*]", - "<ul>\n<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> </li>\n</ul>\n", + "<ul>\n<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> </li>\n</ul>\n", ); } diff -Nru rust-comrak-0.18.0/src/tests/options.rs rust-comrak-0.20.0/src/tests/options.rs --- rust-comrak-0.18.0/src/tests/options.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/options.rs 2006-07-24 01:21:28.000000000 +0000 @@ -5,11 +5,11 @@ let dash = concat!("- a\n"); let plus = concat!("+ a\n"); let star = concat!("* a\n"); - let mut dash_opts = ComrakOptions::default(); + let mut dash_opts = Options::default(); dash_opts.render.list_style = ListStyleType::Dash; - let mut plus_opts = ComrakOptions::default(); + let mut plus_opts = Options::default(); plus_opts.render.list_style = ListStyleType::Plus; - let mut star_opts = ComrakOptions::default(); + let mut star_opts = Options::default(); star_opts.render.list_style = ListStyleType::Star; commonmark(dash, dash, Some(&dash_opts)); @@ -27,7 +27,7 @@ #[test] fn width_breaks() { - let mut options = ComrakOptions::default(); + let mut options = Options::default(); options.render.width = 72; let input = concat!( "this should break because it has breakable characters. break right here newline\n", diff -Nru rust-comrak-0.18.0/src/tests/pathological.rs rust-comrak-0.20.0/src/tests/pathological.rs --- rust-comrak-0.18.0/src/tests/pathological.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/pathological.rs 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,67 @@ +use super::*; +use ntest::timeout; + +// input: python3 -c 'n = 50000; print("*a_ " * n)' +#[test] +#[timeout(4000)] +fn pathological_emphases() { + let n = 50_000; + let input = format!("{}", "*a_ ".repeat(n)); + let mut exp = format!("<p>{}", input); + // Right-most space is trimmed in output. + exp.pop(); + exp += "</p>\n"; + + html(&input, &exp); +} + +// input: python3 -c 'n = 10000; print("|" + "x|" * n + "\n|" + "-|" * n)' +#[test] +#[timeout(4000)] +fn pathological_table_columns_1() { + let n = 100_000; + let input = format!("{}{}{}{}", "|", "x|".repeat(n), "\n|", "-|".repeat(n)); + let exp = format!("<p>{}</p>\n", input); + + html_opts!([extension.table], &input, &exp); +} + +// input: python3 -c 'n = 70000; print("|" + "x|" * n + "\n|" + "-|" * n + "\n" + "a\n" * n)' +#[test] +#[timeout(4000)] +fn pathological_table_columns_2() { + let n = 100_000; + let input = format!( + "{}{}{}{}{}{}", + "|", + "x|".repeat(n), + "\n|", + "-|".repeat(n), + "\n", + "a\n".repeat(n) + ); + + let mut extension = ExtensionOptions::default(); + extension.table = true; + + // Not interested in the actual html, just that we don't timeout + markdown_to_html( + &input, + &Options { + extension, + parse: Default::default(), + render: RenderOptions::default(), + }, + ); +} + +// input: python3 -c 'n = 10000; print("[^1]:" * n + "\n" * n)' +#[test] +#[timeout(4000)] +fn pathological_footnotes() { + let n = 10_000; + let input = format!("{}{}", "[^1]:".repeat(n), "\n".repeat(n)); + let exp = ""; + + html_opts!([extension.footnotes], &input, &exp); +} diff -Nru rust-comrak-0.18.0/src/tests/plugins.rs rust-comrak-0.20.0/src/tests/plugins.rs --- rust-comrak-0.18.0/src/tests/plugins.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/plugins.rs 2006-07-24 01:21:28.000000000 +0000 @@ -42,7 +42,7 @@ "</code></pre>\n" ); - let mut plugins = ComrakPlugins::default(); + let mut plugins = Plugins::default(); let adapter = MockAdapter {}; plugins.render.codefence_syntax_highlighter = Some(&adapter); @@ -68,7 +68,7 @@ } } - let mut plugins = ComrakPlugins::default(); + let mut plugins = Plugins::default(); let adapter = MockAdapter {}; plugins.render.heading_adapter = Some(&adapter); @@ -88,8 +88,8 @@ #[test] #[cfg(feature = "syntect")] -fn syntect_plugin() { - let adapter = crate::plugins::syntect::SyntectAdapter::new("base16-ocean.dark"); +fn syntect_plugin_with_base16_ocean_dark_theme() { + let adapter = crate::plugins::syntect::SyntectAdapter::new(Some("base16-ocean.dark")); let input = concat!("```rust\n", "fn main<'a>();\n", "```\n"); let expected = concat!( @@ -99,7 +99,27 @@ "</code></pre>\n" ); - let mut plugins = ComrakPlugins::default(); + let mut plugins = Plugins::default(); + plugins.render.codefence_syntax_highlighter = Some(&adapter); + + html_plugins(input, expected, &plugins); +} + +#[test] +#[cfg(feature = "syntect")] +fn syntect_plugin_with_css_classes() { + let adapter = crate::plugins::syntect::SyntectAdapter::new(None); + + let input = concat!("```rust\n", "fn main<'a>();\n", "```\n"); + let expected = concat!( + "<pre class=\"syntax-highlighting\"><code class=\"language-rust\">", + "<span class=\"source rust\"><span class=\"meta function rust\"><span class=\"meta function rust\"><span class=\"storage type function rust\">fn</span> </span><span class=\"entity name function rust\">main</span></span><span class=\"meta generic rust\"><span class=\"punctuation definition generic begin rust\">&lt;</span>", + "<span class=\"storage modifier lifetime rust\">&#39;a</span><span class=\"punctuation definition generic end rust\">&gt;</span></span><span class=\"meta function rust\"><span class=\"meta function parameters rust\"><span class=\"punctuation section parameters begin rust\">(</span></span><span class=\"meta function rust\">", + "<span class=\"meta function parameters rust\"><span class=\"punctuation section parameters end rust\">)</span></span></span></span><span class=\"punctuation terminator rust\">;</span>\n</span>", + "</code></pre>\n", + ); + + let mut plugins = Plugins::default(); plugins.render.codefence_syntax_highlighter = Some(&adapter); html_plugins(input, expected, &plugins); diff -Nru rust-comrak-0.18.0/src/tests/propfuzz.rs rust-comrak-0.20.0/src/tests/propfuzz.rs --- rust-comrak-0.18.0/src/tests/propfuzz.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/propfuzz.rs 2006-07-24 01:21:28.000000000 +0000 @@ -6,8 +6,8 @@ #[cfg(not(target_arch = "wasm32"))] #[propfuzz] fn propfuzz_doesnt_crash(md: String) { - let options = ComrakOptions { - extension: ComrakExtensionOptions { + let options = Options { + extension: ExtensionOptions { strikethrough: true, tagfilter: true, table: true, @@ -21,12 +21,13 @@ #[cfg(feature = "shortcodes")] shortcodes: true, }, - parse: ComrakParseOptions { + parse: ParseOptions { smart: true, default_info_string: Some("Rust".to_string()), relaxed_tasklist_matching: true, + relaxed_autolinks: true, }, - render: ComrakRenderOptions { + render: RenderOptions { hardbreaks: true, github_pre_lang: true, full_info_string: true, diff -Nru rust-comrak-0.18.0/src/tests/regressions.rs rust-comrak-0.20.0/src/tests/regressions.rs --- rust-comrak-0.18.0/src/tests/regressions.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/regressions.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,5 +1,4 @@ use super::*; -use ntest::timeout; #[test] fn pointy_brace() { @@ -57,22 +56,6 @@ } #[test] -#[timeout(4000)] -fn pathological_emphases() { - let mut s = String::with_capacity(50000 * 4); - for _ in 0..50000 { - s.push_str("*a_ "); - } - - let mut exp = format!("<p>{}", s); - // Right-most space is trimmed in output. - exp.pop(); - exp += "</p>\n"; - - html(&s, &exp); -} - -#[test] fn no_panic_on_empty_bookended_atx_headers() { html("# #", "<h1></h1>\n"); } @@ -81,18 +64,18 @@ fn no_stack_smash_html() { let s: String = ">".repeat(150_000); let arena = Arena::new(); - let root = parse_document(&arena, &s, &ComrakOptions::default()); + let root = parse_document(&arena, &s, &Options::default()); let mut output = vec![]; - html::format_document(root, &ComrakOptions::default(), &mut output).unwrap() + html::format_document(root, &Options::default(), &mut output).unwrap() } #[test] fn no_stack_smash_cm() { let s: String = ">".repeat(150_000); let arena = Arena::new(); - let root = parse_document(&arena, &s, &ComrakOptions::default()); + let root = parse_document(&arena, &s, &Options::default()); let mut output = vec![]; - cm::format_document(root, &ComrakOptions::default(), &mut output).unwrap() + cm::format_document(root, &Options::default(), &mut output).unwrap() } #[test] diff -Nru rust-comrak-0.18.0/src/tests/tasklist.rs rust-comrak-0.20.0/src/tests/tasklist.rs --- rust-comrak-0.18.0/src/tests/tasklist.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests/tasklist.rs 2006-07-24 01:21:28.000000000 +0000 @@ -25,21 +25,21 @@ concat!( "<ul>\n", "<li><input type=\"checkbox\" disabled=\"\" /> Red</li>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Green</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Green</li>\n", "<li><input type=\"checkbox\" disabled=\"\" /> Blue</li>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Papayawhip</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Papayawhip</li>\n", "</ul>\n", "<!-- end list -->\n", "<ol>\n", "<li><input type=\"checkbox\" disabled=\"\" /> Bird</li>\n", "<li><input type=\"checkbox\" disabled=\"\" /> McHale</li>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Parish</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Parish</li>\n", "</ol>\n", "<!-- end list -->\n", "<ul>\n", "<li><input type=\"checkbox\" disabled=\"\" /> Red\n", "<ul>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Green\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Green\n", "<ul>\n", "<li><input type=\"checkbox\" disabled=\"\" /> Blue</li>\n", "</ul>\n", @@ -58,7 +58,7 @@ "* [!] Red\n", concat!( "<ul>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Red</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Red</li>\n", "</ul>\n" ), ); @@ -74,7 +74,7 @@ "* [!] Red\n", concat!( "<ul>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> Red</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> Red</li>\n", "</ul>\n" ), ); @@ -93,7 +93,7 @@ "<ul>\n", "<li><input type=\"checkbox\" disabled=\"\" /> List item 1</li>\n", "<li><input type=\"checkbox\" disabled=\"\" /> This list item is <strong>bold</strong></li>\n", - "<li><input type=\"checkbox\" disabled=\"\" checked=\"\" /> There is some <code>code</code> here</li>\n", + "<li><input type=\"checkbox\" checked=\"\" disabled=\"\" /> There is some <code>code</code> here</li>\n", "</ul>\n" ), ); diff -Nru rust-comrak-0.18.0/src/tests.rs rust-comrak-0.20.0/src/tests.rs --- rust-comrak-0.18.0/src/tests.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/tests.rs 2006-07-24 01:21:28.000000000 +0000 @@ -6,12 +6,14 @@ mod api; mod autolink; +mod commonmark; mod core; mod description_lists; mod footnotes; mod fuzz; mod header_ids; mod options; +mod pathological; mod plugins; mod propfuzz; mod regressions; @@ -42,9 +44,9 @@ } #[track_caller] -fn commonmark(input: &str, expected: &str, opts: Option<&ComrakOptions>) { +fn commonmark(input: &str, expected: &str, opts: Option<&Options>) { let arena = Arena::new(); - let defaults = ComrakOptions::default(); + let defaults = Options::default(); let options = opts.unwrap_or(&defaults); let root = parse_document(&arena, input, options); @@ -61,16 +63,16 @@ #[track_caller] fn html_opts_i<F>(input: &str, expected: &str, opts: F) where - F: Fn(&mut ComrakOptions), + F: Fn(&mut Options), { - let mut options = ComrakOptions::default(); + let mut options = Options::default(); opts(&mut options); html_opts_w(input, expected, &options); } #[track_caller] -fn html_opts_w(input: &str, expected: &str, options: &ComrakOptions) { +fn html_opts_w(input: &str, expected: &str, options: &Options) { let arena = Arena::new(); let root = parse_document(&arena, input, &options); @@ -104,8 +106,8 @@ }); }; ([all], $lhs:expr, $rhs:expr) => { - $crate::tests::html_opts_w($lhs, $rhs, &$crate::ComrakOptions { - extension: $crate::ComrakExtensionOptions { + $crate::tests::html_opts_w($lhs, $rhs, &$crate::Options { + extension: $crate::ExtensionOptions { strikethrough: true, tagfilter: true, table: true, @@ -118,12 +120,13 @@ front_matter_delimiter: Some("---".to_string()), shortcodes: true, }, - parse: $crate::ComrakParseOptions { + parse: $crate::ParseOptions { smart: true, default_info_string: Some("rust".to_string()), relaxed_tasklist_matching: true, + relaxed_autolinks: true, }, - render: $crate::ComrakRenderOptions { + render: $crate::RenderOptions { hardbreaks: true, github_pre_lang: true, full_info_string: true, @@ -140,9 +143,9 @@ pub(crate) use html_opts; #[track_caller] -fn html_plugins(input: &str, expected: &str, plugins: &ComrakPlugins) { +fn html_plugins(input: &str, expected: &str, plugins: &Plugins) { let arena = Arena::new(); - let options = ComrakOptions::default(); + let options = Options::default(); let root = parse_document(&arena, input, &options); let mut output = vec![]; @@ -173,10 +176,10 @@ #[track_caller] fn xml_opts<F>(input: &str, expected: &str, opts: F) where - F: Fn(&mut ComrakOptions), + F: Fn(&mut Options), { let arena = Arena::new(); - let mut options = ComrakOptions::default(); + let mut options = Options::default(); opts(&mut options); let root = parse_document(&arena, input, &options); @@ -246,9 +249,9 @@ #[track_caller] fn assert_ast_match_i<F>(md: &str, amt: AstMatchTree, opts: F) where - F: Fn(&mut ComrakOptions), + F: Fn(&mut Options), { - let mut options = ComrakOptions::default(); + let mut options = Options::default(); options.render.sourcepos = true; opts(&mut options); diff -Nru rust-comrak-0.18.0/src/xml.rs rust-comrak-0.20.0/src/xml.rs --- rust-comrak-0.18.0/src/xml.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-comrak-0.20.0/src/xml.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,25 +1,28 @@ -use crate::nodes::{AstNode, ListType, NodeCode, NodeValue}; -use crate::parser::{ComrakOptions, ComrakPlugins}; +use crate::nodes::{AstNode, ListType, NodeCode, NodeTable, NodeValue}; +use crate::parser::{Options, Plugins}; use once_cell::sync::Lazy; +use std::cmp; use std::io::{self, Write}; use crate::nodes::NodeHtmlBlock; +const MAX_INDENT: u32 = 40; + /// Formats an AST as HTML, modified by the given options. pub fn format_document<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, ) -> io::Result<()> { - format_document_with_plugins(root, options, output, &ComrakPlugins::default()) + format_document_with_plugins(root, options, output, &Plugins::default()) } /// Formats an AST as HTML, modified by the given options. Accepts custom plugins. pub fn format_document_with_plugins<'a>( root: &'a AstNode<'a>, - options: &ComrakOptions, + options: &Options, output: &mut dyn Write, - plugins: &ComrakPlugins, + plugins: &Plugins, ) -> io::Result<()> { output.write_all(b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")?; output.write_all(b"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n")?; @@ -29,17 +32,13 @@ struct XmlFormatter<'o> { output: &'o mut dyn Write, - options: &'o ComrakOptions, - _plugins: &'o ComrakPlugins<'o>, + options: &'o Options, + _plugins: &'o Plugins<'o>, indent: u32, } impl<'o> XmlFormatter<'o> { - fn new( - options: &'o ComrakOptions, - output: &'o mut dyn Write, - plugins: &'o ComrakPlugins, - ) -> Self { + fn new(options: &'o Options, output: &'o mut dyn Write, plugins: &'o Plugins) -> Self { XmlFormatter { options, output, @@ -125,7 +124,7 @@ } fn indent(&mut self) -> io::Result<()> { - for _ in 0..self.indent { + for _ in 0..(cmp::min(self.indent, MAX_INDENT)) { self.output.write_all(b" ")?; } Ok(()) @@ -219,23 +218,25 @@ let header_row = &ancestors.next().unwrap().data.borrow().value; let table = &ancestors.next().unwrap().data.borrow().value; - if let (NodeValue::TableRow(true), NodeValue::Table(aligns)) = - (header_row, table) + if let ( + NodeValue::TableRow(true), + NodeValue::Table(NodeTable { alignments, .. }), + ) = (header_row, table) { let ix = node.preceding_siblings().count() - 1; - if let Some(xml_align) = aligns[ix].xml_name() { + if let Some(xml_align) = alignments[ix].xml_name() { write!(self.output, " align=\"{}\"", xml_align)?; } } } NodeValue::FootnoteDefinition(ref fd) => { self.output.write_all(b" label=\"")?; - self.escape(fd.as_bytes())?; + self.escape(fd.name.as_bytes())?; self.output.write_all(b"\"")?; } - NodeValue::FootnoteReference(ref fr) => { + NodeValue::FootnoteReference(ref nfr) => { self.output.write_all(b" label=\"")?; - self.escape(fr.as_bytes())?; + self.escape(nfr.name.as_bytes())?; self.output.write_all(b"\"")?; } NodeValue::TaskItem(Some(_)) => {