10 |
11 | inline html element
12 |
--------------------------------------------------------------------------------
/tests/fixtures/super-sub-script.md:
--------------------------------------------------------------------------------
1 | this is an example of superscript.
2 |
3 | this is an example of subscript.
4 |
5 | 1st of the month.
6 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-nested-output:
--------------------------------------------------------------------------------
1 |
2 |
3 | *emphasized*
4 |
5 |
6 |
7 |
8 | second line
9 |
10 |
11 | inline html element
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-super-sub-script-output:
--------------------------------------------------------------------------------
1 | this is an example of superscript.
2 |
3 | this is an example of subscript.
4 |
5 | 1st of the month.
--------------------------------------------------------------------------------
/tests/fixtures/heading-id-classes.md:
--------------------------------------------------------------------------------
1 | # nothing
2 |
3 | # with ID {#myh1}
4 |
5 | ## with class {.classh2}
6 |
7 | ### multiple {#myh3 .classh3}
8 |
9 | # text { #id .class1 .class2 myattr, other_attr=myvalue }
10 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-heading-id-classes-output:
--------------------------------------------------------------------------------
1 | # nothing
2 |
3 | # with ID { #myh1 }
4 |
5 | ## with class { .classh2 }
6 |
7 | ### multiple { #myh3 .classh3 }
8 |
9 | # text { #id .class1 .class2 myattr, other_attr=myvalue }
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-table-with-escapes-output:
--------------------------------------------------------------------------------
1 | |First|Second|Third|Fourth|
2 | |-----|------|-----|------|
3 | |`\|`|`\\`|`a\[b\]`|`>>`|
4 |
5 | Normal inline code with pipes `|` should not be re-escaped, because it does not
6 | cause a problem!
--------------------------------------------------------------------------------
/tests/fixtures/table-with-escapes.md:
--------------------------------------------------------------------------------
1 | | First | Second | Third | Fourth |
2 | | ----- | ------ | -------- | ------ |
3 | | `\|` | `\\` | `a\[b\]` | `>>` |
4 |
5 | Normal inline code with pipes `|` should not be re-escaped, because it does not
6 | cause a problem!
7 |
--------------------------------------------------------------------------------
/tests/fixtures/unordered.md:
--------------------------------------------------------------------------------
1 | Unordered lists:
2 |
3 | * Lorem impsum
4 | * Nested
5 | * Inline
6 | * Text
7 | * dolor sit amet
8 | * Nested
9 |
10 | * With
11 |
12 | Paragraphs and nested blocks:
13 |
14 | > A quote
15 |
16 | And some text at the end
17 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-unordered-output:
--------------------------------------------------------------------------------
1 | Unordered lists:
2 |
3 | * Lorem impsum
4 | * Nested
5 | * Inline
6 | * Text
7 | * dolor sit amet
8 | * Nested
9 |
10 | * With
11 |
12 | Paragraphs and nested blocks:
13 |
14 | >
15 | > A quote
16 |
17 | And some text at the end
--------------------------------------------------------------------------------
/tests/fixtures/ordered.md:
--------------------------------------------------------------------------------
1 | Ordered lists:
2 |
3 | 1. Lorem impsum
4 | 1. Nested
5 | 2. Inline
6 | 3. Text
7 | 2. dolor sit amet
8 | 1. Nested
9 |
10 | 2. With
11 |
12 | Paragraphs and nested blocks:
13 |
14 | > A quote
15 |
16 | And some text at the end
17 | 3. consetetur sadipscing elitr
--------------------------------------------------------------------------------
/tests/fixtures/lists-nested.md:
--------------------------------------------------------------------------------
1 | 1. list paragraph 1
2 | ```
3 | code sample
4 | ```
5 | 2. list paragraph 2
6 |
7 | ---
8 |
9 | 1. list paragraph 1
10 | ```
11 | code sample
12 | ```
13 |
14 | 2. list paragraph 2
15 |
16 | ---
17 |
18 | 1. list paragraph 1
19 |
20 | code sample
21 |
22 | 2. list paragraph 2
23 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-lists-nested-output:
--------------------------------------------------------------------------------
1 | 1. list paragraph 1
2 | ````
3 | code sample
4 | ````
5 |
6 | 1. list paragraph 2
7 |
8 | ---
9 |
10 | 1. list paragraph 1
11 |
12 | ````
13 | code sample
14 | ````
15 |
16 | 1. list paragraph 2
17 |
18 | ---
19 |
20 | 1. list paragraph 1
21 |
22 | code sample
23 |
24 |
25 | 1. list paragraph 2
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-ordered-output:
--------------------------------------------------------------------------------
1 | Ordered lists:
2 |
3 | 1. Lorem impsum
4 |
5 | 1. Nested
6 | 1. Inline
7 | 1. Text
8 | 1. dolor sit amet
9 |
10 | 1. Nested
11 |
12 | 1. With
13 |
14 | Paragraphs and nested blocks:
15 |
16 | >
17 | > A quote
18 |
19 | And some text at the end
20 |
21 | 1. consetetur sadipscing elitr
--------------------------------------------------------------------------------
/tests/spec/CommonMark/README.google:
--------------------------------------------------------------------------------
1 | URL: https://github.com/jgm/CommonMark.git
2 | Version: 1ef46a73e61968a60cc7b2e700381d719165b86c
3 | License: Creative Commons CC-BY-SA 4.0
4 | License File: LICENSE
5 |
6 | Description:
7 | CommonMark spec
8 |
9 | Local Modifications:
10 | This directory contains only the spec file. License file has been
11 | subsetted to only the files it applies to, and text of CC-BY-SA 4.0
12 | license has been added.
13 |
--------------------------------------------------------------------------------
/tests/fixtures/definition-list.md:
--------------------------------------------------------------------------------
1 | A paragraph
2 |
3 | A Term
4 | : Defined by block content
5 |
6 | - an embedded list entry
7 | - another entry
8 |
9 | Another Term
10 | : This one even has an embedded definition list
11 |
12 | A sub-term
13 | : It's definition
14 |
15 | Another sub-term
16 | : With a definition
17 |
18 |
19 | Back to a normal paragraph.
20 |
21 | ---
22 |
23 | > A quoted paragraph
24 | >
25 | > A quoted term
26 | > : its definition
27 | >
28 | > Another quoted term
29 | > : and its definition
30 | >
31 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-definition-list-output:
--------------------------------------------------------------------------------
1 | A paragraph
2 |
3 |
4 | A Term
5 | : Defined by block content
6 |
7 | * an embedded list entry
8 | * another entry
9 |
10 |
11 |
12 | Another Term
13 | : This one even has an embedded definition list
14 |
15 |
16 | A sub-term
17 | : It's definition
18 |
19 | Another sub-term
20 | : With a definition
21 |
22 |
23 |
24 | Back to a normal paragraph.
25 |
26 | ---
27 |
28 | >
29 | > A quoted paragraph
30 | >
31 | >
32 | > A quoted term
33 | > : its definition
34 | >
35 | > Another quoted term
36 | > : and its definition
37 | >
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build-and-test:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: cargo fmt
15 | run: |
16 | cargo fmt --check
17 | - name: tests
18 | run: |
19 | make tests
20 |
21 | msrv:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v4
25 | - uses: taiki-e/install-action@cargo-hack
26 | - run: cargo hack --rust-version --no-dev-deps check
27 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | help: ## Display this help
3 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
4 |
5 | .PHONY: docs unittests journeytests tests
6 |
7 | ##@ Testing
8 |
9 | docs: ## Builds docs
10 | cargo doc
11 |
12 | unittests: ## run unit tests
13 | cargo test --all
14 |
15 | clippy: ## run clippy
16 | cargo clippy
17 |
18 | journeytests: ## run journey tests
19 | ./tests/cat.sh
20 |
21 |
22 | tests: docs clippy unittests journeytests ## run all tests
23 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pulldown-cmark-to-cmark"
3 | version = "21.1.0"
4 | authors = [
5 | "Sebastian Thiel ",
6 | "Dylan Owen ",
7 | "Alessandro Ogier ",
8 | "Zixian Cai <2891235+caizixian@users.noreply.github.com>",
9 | "Andrew Lyjak "]
10 |
11 | description = "Convert pulldown-cmark Events back to the string they were parsed from"
12 | license = "Apache-2.0"
13 | keywords = ["markdown", "common-mark", "render", "converter"]
14 | repository = "https://github.com/Byron/pulldown-cmark-to-cmark"
15 | homepage = "https://github.com/Byron/pulldown-cmark-to-cmark"
16 | documentation = "https://docs.rs/crate/pulldown-cmark-to-cmark"
17 | readme = "README.md"
18 | edition = "2018"
19 | include = ["src/*.rs", "LICENSE-APACHE", "README.md", "CHANGELOG.md"]
20 | # This follows the MSRV of `pulldown-cmark`
21 | rust-version = "1.71.1"
22 |
23 | [dependencies]
24 | pulldown-cmark = { version = "0.13.0", default-features = false }
25 |
26 | [dev-dependencies]
27 | indoc = "2.0.5"
28 | pretty_assertions = "1.4.0"
29 | yansi = "1.0.1"
30 |
--------------------------------------------------------------------------------
/examples/stupicat.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | env,
3 | io::{stdout, Write},
4 | };
5 |
6 | use pulldown_cmark::{Options, Parser};
7 | use pulldown_cmark_to_cmark::{cmark, cmark_resume};
8 |
9 | fn main() -> Result<(), Box> {
10 | let path = env::args_os()
11 | .nth(1)
12 | .expect("First argument is markdown file to display");
13 | let event_by_event = env::var_os("STUPICAT_STATE_TEST").is_some();
14 |
15 | let md = std::fs::read_to_string(&path)?;
16 | let mut buf = String::with_capacity(md.len() + 128);
17 | let mut options = Options::all();
18 | options.remove(Options::ENABLE_SMART_PUNCTUATION);
19 |
20 | if event_by_event {
21 | let mut state = None;
22 | for event in Parser::new_ext(&md, options) {
23 | state = cmark_resume(std::iter::once(event), &mut buf, state.take())?.into();
24 | }
25 | if let Some(state) = state {
26 | state.finalize(&mut buf)?;
27 | }
28 | } else {
29 | cmark(Parser::new_ext(&md, options), &mut buf)?;
30 | }
31 |
32 | stdout().write_all(buf.as_bytes())?;
33 | Ok(())
34 | }
35 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-table-output:
--------------------------------------------------------------------------------
1 | Colons can be used to align columns.
2 |
3 | |Tables|Are|Cool|yo|
4 | |------|:-:|---:|:-|
5 | |col 3 is|right-aligned|$1600|x|
6 | |col 2 is|centered|$12|y|
7 | |zebra stripes|are neat|$1|z|
8 |
9 | There must be at least 3 dashes separating each header cell.
10 | The outer pipes (|) are optional, and you don't need to make the
11 | raw Markdown line up prettily. You can also use inline Markdown.
12 |
13 | >
14 | > |Markdown|Less|Pretty|
15 | > |--------|----|------|
16 | > |*Still*|`renders`|**nicely**|
17 | > |1|2|3|
18 |
19 | |Target|std|rustc|cargo|notes|
20 | |------|---|-----|-----|-----|
21 | |`x86_64-unknown-linux-musl`|✓|||64-bit Linux with MUSL|
22 | |`arm-linux-androideabi`|✓|||ARM Android|
23 | |`arm-unknown-linux-gnueabi`|✓|✓||ARM Linux (2.6.18+)|
24 | |`arm-unknown-linux-gnueabihf`|✓|✓||ARM Linux (2.6.18+)|
25 | |`aarch64-unknown-linux-gnu`|✓|||ARM64 Linux (2.6.18+)|
26 | |`mips-unknown-linux-gnu`|✓|||MIPS Linux (2.6.18+)|
27 | |`mipsel-unknown-linux-gnu`|✓|||MIPS (LE) Linux (2.6.18+)|
28 |
29 | Tables with formatting in header
30 |
31 | |header|`code`|*emphasize*|*strong*|
32 | |------|------|---------|------|
33 | |data 1|data 2|data 3|data 4|
--------------------------------------------------------------------------------
/tests/fixtures/table.md:
--------------------------------------------------------------------------------
1 | Colons can be used to align columns.
2 |
3 | | Tables | Are | Cool | yo |
4 | |---------------|:-------------:|------:|:---|
5 | | col 3 is | right-aligned | $1600 | x |
6 | | col 2 is | centered | $12 | y |
7 | | zebra stripes | are neat | $1 | z |
8 |
9 | There must be at least 3 dashes separating each header cell.
10 | The outer pipes (|) are optional, and you don't need to make the
11 | raw Markdown line up prettily. You can also use inline Markdown.
12 |
13 | > Markdown | Less | Pretty
14 | > --- | --- | ---
15 | > *Still* | `renders` | **nicely**
16 | > 1 | 2 | 3
17 |
18 | | Target | std |rustc|cargo| notes |
19 | |-------------------------------|-----|-----|-----|----------------------------|
20 | | `x86_64-unknown-linux-musl` | ✓ | | | 64-bit Linux with MUSL |
21 | | `arm-linux-androideabi` | ✓ | | | ARM Android |
22 | | `arm-unknown-linux-gnueabi` | ✓ | ✓ | | ARM Linux (2.6.18+) |
23 | | `arm-unknown-linux-gnueabihf` | ✓ | ✓ | | ARM Linux (2.6.18+) |
24 | | `aarch64-unknown-linux-gnu` | ✓ | | | ARM64 Linux (2.6.18+) |
25 | | `mips-unknown-linux-gnu` | ✓ | | | MIPS Linux (2.6.18+) |
26 | | `mipsel-unknown-linux-gnu` | ✓ | | | MIPS (LE) Linux (2.6.18+) |
27 |
28 | Tables with formatting in header
29 |
30 | | header | `code` | _emphasize_ | *strong* |
31 | | ------ | ------ | ----------- | -------- |
32 | | data 1 | data 2 | data 3 | data 4 |
33 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ### How to contribute
2 |
3 | * [fork this project][fork] on github
4 | * For setting up the environment to run the self tests, look at `.github/workflows/rust.yml`.
5 | * **Write a test that fails unless your patch is present.**
6 | * There are fixture-based tests run by [`cat.sh`][sh-tests].
7 | * There are [unit-level tests][unit-tests] run by `cargo test`.
8 | * **Write the patch to fix the test**.
9 | * Add yourself to the `authors` line in the [`Cargo.toml`][cargo-authors] file.
10 | * Initiate a pull request
11 |
12 | [fork]: https://github.com/Byron/pulldown-cmark-to-cmark/fork
13 | [cargo-authors]: https://github.com/Byron/pulldown-cmark-to-cmark/blob/master/Cargo.toml#L4
14 | [unit-tests]: https://github.com/Byron/pulldown-cmark-to-cmark/blob/76667725b61be24890fbdfed5e7ecdb4c1ad1dc8/tests/fmt.rs#L146
15 | [sh-tests]: https://github.com/Byron/pulldown-cmark-to-cmark/blob/76667725b61be24890fbdfed5e7ecdb4c1ad1dc8/tests/cat.sh#L16
16 |
17 | ### CommonMark Conformance Results
18 |
19 | The `cargo test` suite will automatically test that `pulldown-cmark-to-cmark`
20 | has maintained its expected conformance level against the example Markdown
21 | programs in the CommonMark specification. As of time of writing,
22 | **`pulldown-cmark-to-cmark` passes ~90%** of the CommonMark example program tests.
23 |
24 | We would love your help in improving our conformance! 🙂
25 |
26 | To view which conformance tests could use your help to fix, run:
27 |
28 | ```shell
29 | $ FULL_CMARK_RESULTS=true cargo test
30 | ```
31 |
32 | This will show a visual report of each failing CommonMark conformance test,
33 | including the original Markdown, the Markdown generated by
34 | `pulldown-cmark-to-cmark`, the expected HTML result, and a diff of the `Event`
35 | streams of the original and regenerated Markdown.
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://crates.io/crates/pulldown-cmark-to-cmark)
2 | 
3 |
4 | A utility library which translates [`Event`][pdcm-event] back to markdown.
5 | It's the prerequisite for writing markdown filters which can work as
6 | [mdbook-preprocessors][mdbook-prep].
7 |
8 | This library takes great pride in supporting **everything that `pulldown-cmark`** supports,
9 | including *tables* and *footnotes* and *codeblocks in codeblocks*,
10 | while assuring *quality* with a powerful test suite.
11 |
12 | [pdcm-event]: https://docs.rs/pulldown-cmark/latest/pulldown_cmark/enum.Event.html
13 | [mdbook-prep]: https://rust-lang.github.io/mdBook/for_developers/preprocessors.html
14 |
15 | ### How to use
16 |
17 | Please have a look at the [`stupicat`-example][sc-example] for a complete tour
18 | of the API, or have a look at the [api-docs][api].
19 |
20 | It's easiest to get this library into your `Cargo.toml` using `cargo-add`:
21 | ```
22 | cargo add pulldown-cmark-to-cmark
23 | ```
24 |
25 | [sc-example]: https://github.com/Byron/pulldown-cmark-to-cmark/blob/76667725b61be24890fbdfed5e7ecdb4c1ad1dc8/examples/stupicat.rs#L21
26 | [api]: https://docs.rs/crate/pulldown-cmark-to-cmark
27 |
28 | ### Supported Rust Versions
29 |
30 | `pulldown-cmark-to-cmark` follows the MSRV (minimum supported rust version) policy of [`pulldown-cmark`]. The current MSRV is 1.71.1.
31 |
32 | [`pulldown-cmark`]: https://github.com/pulldown-cmark/pulldown-cmark
33 |
34 | ### Friends of this project
35 |
36 | * [**termbook**](https://github.com/Byron/termbook)
37 | * A runner for `mdbooks` to keep your documentation tested.
38 | * [**Share Secrets Safely**](https://github.com/Byron/share-secrets-safely)
39 | * share secrets within teams to avoid plain-text secrets from day one
40 |
41 | ### Maintenance Guide
42 |
43 | #### Making a new release
44 |
45 | * **Assure all documentation is up-to-date and tests are green**
46 | * update the `version` in `Cargo.toml` and `git commit`
47 | * run `cargo release --no-dev-version`
48 |
--------------------------------------------------------------------------------
/tests/utilities.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | WHITE="$(tput setaf 9 || echo -n '')"
4 | YELLOW="$(tput setaf 3 || echo -n '')"
5 | GREEN="$(tput setaf 2 || echo -n '')"
6 | RED="$(tput setaf 1 || echo -n '')"
7 | OFFSET=( )
8 | STEP=" "
9 |
10 | function title () {
11 | echo "$WHITE-----------------------------------------------------"
12 | echo "${GREEN}$*"
13 | echo "$WHITE-----------------------------------------------------"
14 | }
15 |
16 | function _context () {
17 | local name="${1:?}"
18 | shift
19 | echo 1>&2 "${YELLOW}${OFFSET[*]:-}[$name] $*"
20 | OFFSET+=("$STEP")
21 | }
22 |
23 | function with () {
24 | _context with "$*"
25 | }
26 |
27 | function when () {
28 | _context when "$*"
29 | }
30 |
31 | function _note () {
32 | local name="${1:?}"
33 | local color="${2:-}"
34 | shift 2
35 | echo 1>&2 -n "${OFFSET[*]:-}${color}[$name] ${*// /}"
36 | }
37 |
38 | function it () {
39 | _note it "${GREEN}" "$*"
40 | }
41 |
42 | function precondition () {
43 | _note precondition "${WHITE}" "$*"
44 | }
45 |
46 | function shortcoming () {
47 | _note shortcoming "${RED}" "$*"
48 | }
49 |
50 | function fail () {
51 | echo 1>&2 "${RED} $*"
52 | exit 1
53 | }
54 |
55 | function sandbox () {
56 | sandbox_tempdir="$(mktemp -t sandbox.XXXX -d)"
57 | # shellcheck disable=2064
58 | trap "popd >/dev/null" EXIT
59 | pushd "$sandbox_tempdir" >/dev/null \
60 | || fail "Could not change directory into temporary directory."
61 |
62 | local custom_init="${1:-}"
63 | if [ -n "$custom_init" ]; then
64 | eval "$custom_init"
65 | fi
66 | }
67 |
68 | function expect_equals () {
69 | expect_run 0 test "${1:?}" = "${2:?}"
70 | }
71 |
72 | function expect_exists () {
73 | expect_run 0 test -e "${1:?}"
74 | }
75 |
76 | function expect_run_sh () {
77 | expect_run "${1:?}" bash -c "${2:?}"
78 | }
79 |
80 | function expect_snapshot () {
81 | local expected=${1:?}
82 | local actual=${2:?}
83 | if ! [ -e "$expected" ]; then
84 | cp -R "$actual" "$expected"
85 | fi
86 | expect_run 0 diff -r "$expected" "$actual"
87 | }
88 |
89 | function expect_run () {
90 | local expected_exit_code=$1
91 | shift
92 | local output=
93 | set +e
94 | output="$("$@" 2>&1)"
95 |
96 | local actual_exit_code=$?
97 | if [[ "$actual_exit_code" == "$expected_exit_code" ]]; then
98 | if [[ -n "${WITH_SNAPSHOT-}" ]]; then
99 | local expected="$WITH_SNAPSHOT"
100 | if ! [ -f "$expected" ]; then
101 | echo -n "$output" > "$expected" || exit 1
102 | fi
103 | if ! diff "$expected" <(echo -n "$output"); then
104 | echo 1>&2 "${RED} - FAIL"
105 | echo 1>&2 "${WHITE}\$$*"
106 | echo 1>&2 "Output snapshot did not match snapshot at '$expected'"
107 | echo 1>&2 "$output"
108 | exit 1
109 | fi
110 | fi
111 | echo 1>&2
112 | else
113 | echo 1>&2 "${RED} - FAIL"
114 | echo 1>&2 "${WHITE}\$$*"
115 | echo 1>&2 "${RED}Expected actual status $actual_exit_code to be $expected_exit_code"
116 | echo 1>&2 "$output"
117 | exit 1
118 | fi
119 | set -e
120 | }
121 |
--------------------------------------------------------------------------------
/tests/integrate/main.rs:
--------------------------------------------------------------------------------
1 | mod display;
2 | mod fmt;
3 | mod spec;
4 |
5 | #[cfg(test)]
6 | mod fuzzed {
7 | use pulldown_cmark::{Event, HeadingLevel, Tag, TagEnd};
8 | use pulldown_cmark_to_cmark::{cmark, Error};
9 |
10 | #[test]
11 | fn cmark_with_invalid_event_stream() {
12 | let events = [
13 | Event::Start(Tag::Heading {
14 | level: HeadingLevel::H2,
15 | id: None,
16 | classes: vec![],
17 | attrs: vec![],
18 | }),
19 | Event::Start(Tag::Heading {
20 | level: HeadingLevel::H2,
21 | id: None,
22 | classes: vec![],
23 | attrs: vec![],
24 | }),
25 | Event::Text(pulldown_cmark::CowStr::Borrowed("hello")),
26 | Event::End(TagEnd::Heading(HeadingLevel::H2)),
27 | Event::End(TagEnd::Heading(HeadingLevel::H2)),
28 | ];
29 | assert!(matches!(
30 | cmark(events.iter(), String::new()),
31 | Err(Error::UnexpectedEvent)
32 | ));
33 | }
34 | }
35 |
36 | #[cfg(test)]
37 | mod calculate_code_block_token_count {
38 | use pulldown_cmark::{CodeBlockKind, CowStr, Event, Tag, TagEnd};
39 | use pulldown_cmark_to_cmark::calculate_code_block_token_count;
40 |
41 | const CODE_BLOCK_START: Event<'_> = Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(CowStr::Borrowed(""))));
42 | const CODE_BLOCK_END: Event<'_> = Event::End(TagEnd::CodeBlock);
43 |
44 | #[test]
45 | fn no_token() {
46 | let events = &[CODE_BLOCK_START, Event::Text("text".into()), CODE_BLOCK_END];
47 | assert_eq!(calculate_code_block_token_count(events.iter()), None);
48 | }
49 |
50 | #[test]
51 | fn backtick() {
52 | let events = &[CODE_BLOCK_START, Event::Text("```".into()), CODE_BLOCK_END];
53 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(4));
54 |
55 | let events = &[CODE_BLOCK_START, Event::Text("````".into()), CODE_BLOCK_END];
56 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(5));
57 |
58 | let events = &[CODE_BLOCK_START, Event::Text("``````````".into()), CODE_BLOCK_END];
59 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(11));
60 | }
61 |
62 | #[test]
63 | fn tilde() {
64 | let events = &[CODE_BLOCK_START, Event::Text("~~~".into()), CODE_BLOCK_END];
65 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(4));
66 |
67 | let events = &[CODE_BLOCK_START, Event::Text("~~~~".into()), CODE_BLOCK_END];
68 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(5));
69 |
70 | let events = &[CODE_BLOCK_START, Event::Text("~~~~~~~~~~".into()), CODE_BLOCK_END];
71 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(11));
72 | }
73 |
74 | #[test]
75 | fn mix() {
76 | let events = &[CODE_BLOCK_START, Event::Text("```~~~~".into()), CODE_BLOCK_END];
77 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(5));
78 |
79 | let events = &[CODE_BLOCK_START, Event::Text("~~~~`````~~".into()), CODE_BLOCK_END];
80 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(6));
81 |
82 | let events = &[
83 | CODE_BLOCK_START,
84 | Event::Text("~~~```````~~~```~~".into()),
85 | CODE_BLOCK_END,
86 | ];
87 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(8));
88 | }
89 |
90 | #[test]
91 | fn splitted_text() {
92 | let events = &[
93 | CODE_BLOCK_START,
94 | Event::Text("~~~".into()),
95 | Event::Text("~~~".into()),
96 | CODE_BLOCK_END,
97 | ];
98 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(7));
99 |
100 | let events = &[
101 | CODE_BLOCK_START,
102 | Event::Text("````".into()),
103 | Event::Text("````".into()),
104 | CODE_BLOCK_END,
105 | ];
106 | assert_eq!(calculate_code_block_token_count(events.iter()), Some(9));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/cat.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -eu -o pipefail
4 | exe=(cargo run --example stupicat --)
5 |
6 | root="$(cd "${0%/*}" && pwd)"
7 | # shellcheck source=./tests/utilities.sh
8 | source "$root/utilities.sh"
9 |
10 | SUCCESSFULLY=0
11 |
12 | fixture="$root/fixtures"
13 | snapshot="$fixture/snapshots"
14 |
15 | title "stupicat"
16 |
17 | (with "a mathematical expression"
18 | it "succeeds" && \
19 | WITH_SNAPSHOT="$snapshot/stupicat-math-output" \
20 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/math.md 2>/dev/null"
21 | )
22 |
23 | (with "a table"
24 | it "succeeds" && \
25 | WITH_SNAPSHOT="$snapshot/stupicat-table-output" \
26 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/table.md 2>/dev/null"
27 | )
28 |
29 | (with "a more complex ordered list"
30 | it "succeeds" && \
31 | WITH_SNAPSHOT="$snapshot/stupicat-ordered-output" \
32 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/ordered.md 2>/dev/null"
33 | )
34 |
35 | (with "a more complex unordered list"
36 | it "succeeds" && \
37 | WITH_SNAPSHOT="$snapshot/stupicat-unordered-output" \
38 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/unordered.md 2>/dev/null"
39 | )
40 |
41 | (with "a standard common-mark example file"
42 | (when "processing all events in one invocation"
43 | it "succeeds" && \
44 | WITH_SNAPSHOT="$snapshot/stupicat-output" \
45 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/common-mark.md 2>/dev/null"
46 | )
47 | (when "processing event by event"
48 | it "succeeds" && \
49 | STUPICAT_STATE_TEST=1 \
50 | WITH_SNAPSHOT="$snapshot/stupicat-event-by-event-output" \
51 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/common-mark.md 2>/dev/null"
52 | )
53 | )
54 |
55 | (with "markdown and html nested"
56 | it "succeeds" && \
57 | WITH_SNAPSHOT="$snapshot/stupicat-nested-output" \
58 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/nested.md 2>/dev/null"
59 | )
60 |
61 | (with "lists and nested content"
62 | it "succeeds" && \
63 | WITH_SNAPSHOT="$snapshot/stupicat-lists-nested-output" \
64 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/lists-nested.md 2>/dev/null"
65 | )
66 |
67 | (with "table with html"
68 | it "succeeds" && \
69 | WITH_SNAPSHOT="$snapshot/stupicat-table-with-html-output" \
70 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/table-with-html.md 2>/dev/null"
71 | )
72 |
73 | (with "heading with identifier and classes"
74 | it "succeeds" && \
75 | WITH_SNAPSHOT="$snapshot/stupicat-heading-id-classes-output" \
76 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/heading-id-classes.md 2>/dev/null"
77 | )
78 |
79 | (with "repeated shortcut links"
80 | it "succeeds" && \
81 | WITH_SNAPSHOT="$snapshot/stupicat-repeated-shortcut-links-output" \
82 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/repeated-shortcut-links.md 2>/dev/null"
83 | )
84 |
85 | (with "indented code block"
86 | it "succeeds" && \
87 | WITH_SNAPSHOT="$snapshot/stupicat-indented-code-block" \
88 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/indented-code-block.md 2>/dev/null"
89 | )
90 |
91 | (with "yaml frontmatter"
92 | it "succeeds" && \
93 | WITH_SNAPSHOT="$snapshot/stupicat-yaml-frontmatter-output" \
94 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/yaml-frontmatter.md 2>/dev/null"
95 | )
96 |
97 | (with "toml frontmatter"
98 | it "succeeds" && \
99 | WITH_SNAPSHOT="$snapshot/stupicat-toml-frontmatter-output" \
100 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/toml-frontmatter.md 2>/dev/null"
101 | )
102 |
103 | (with "table with escaped characters in cells"
104 | it "succeeds in reproducing the escapes" && \
105 | WITH_SNAPSHOT="$snapshot/stupicat-table-with-escapes-output" \
106 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/table-with-escapes.md 2>/dev/null"
107 | )
108 |
109 | (with "definition lists"
110 | it "succeeds in reproducing definition-list indentation" && \
111 | WITH_SNAPSHOT="$snapshot/stupicat-definition-list-output" \
112 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/definition-list.md 2>/dev/null"
113 | )
114 |
115 | (with "super and subscript"
116 | it "succeeds" && \
117 | WITH_SNAPSHOT="$snapshot/stupicat-super-sub-script-output" \
118 | expect_run_sh $SUCCESSFULLY "${exe[*]} $fixture/super-sub-script.md 2>/dev/null"
119 | )
120 |
--------------------------------------------------------------------------------
/tests/spec/CommonMark/smart_punct.txt:
--------------------------------------------------------------------------------
1 | ## Smart punctuation
2 |
3 | Open quotes are matched with closed quotes.
4 | The same method is used for matching openers and closers
5 | as is used in emphasis parsing:
6 |
7 | ```````````````````````````````` example_smartpunct
8 | "Hello," said the spider.
9 | "'Shelob' is my name."
10 | .
11 |
“Hello,” said the spider.
12 | “‘Shelob’ is my name.”
33 | ````````````````````````````````
34 |
35 | A single quote that isn't an open quote matched
36 | with a close quote will be treated as an
37 | apostrophe:
38 |
39 | ```````````````````````````````` example_smartpunct
40 | Were you alive in the 70's?
41 | .
42 |
Were you alive in the 70’s?
43 | ````````````````````````````````
44 |
45 | ```````````````````````````````` example_smartpunct
46 | Here is some quoted '`code`' and a "[quoted link](url)".
47 | .
48 |
49 | ````````````````````````````````
50 |
51 | Here the first `'` is treated as an apostrophe, not
52 | an open quote, because the final single quote is matched
53 | by the single quote before `jolly`:
54 |
55 | ```````````````````````````````` example_smartpunct
56 | 'tis the season to be 'jolly'
57 | .
58 |
’tis the season to be ‘jolly’
59 | ````````````````````````````````
60 |
61 | Multiple apostrophes should not be marked as open/closing quotes.
62 |
63 | ```````````````````````````````` example_smartpunct
64 | 'We'll use Jane's boat and John's truck,' Jenna said.
65 | .
66 |
‘We’ll use Jane’s boat and John’s truck,’ Jenna said.
67 | ````````````````````````````````
68 |
69 | An unmatched double quote will be interpreted as a
70 | left double quote, to facilitate this style:
71 |
72 | ```````````````````````````````` example_smartpunct
73 | "A paragraph with no closing quote.
74 |
75 | "Second paragraph by same speaker, in fiction."
76 | .
77 |
“A paragraph with no closing quote.
78 |
“Second paragraph by same speaker, in fiction.”
79 | ````````````````````````````````
80 |
81 | A quote following a `]` or `)` character cannot
82 | be an open quote:
83 |
84 | ```````````````````````````````` example_smartpunct
85 | [a]'s b'
86 | .
87 |
[a]’s b’
88 | ````````````````````````````````
89 |
90 | Quotes that are escaped come out as literal straight
91 | quotes:
92 |
93 | ```````````````````````````````` example_smartpunct
94 | \"This is not smart.\"
95 | This isn\'t either.
96 | 5\'8\"
97 | .
98 |
"This is not smart."
99 | This isn't either.
100 | 5'8"
101 | ````````````````````````````````
102 |
103 | Two hyphens form an en-dash, three an em-dash.
104 |
105 | ```````````````````````````````` example_smartpunct
106 | Some dashes: em---em
107 | en--en
108 | em --- em
109 | en -- en
110 | 2--3
111 | .
112 |
Some dashes: em—em
113 | en–en
114 | em — em
115 | en – en
116 | 2–3
117 | ````````````````````````````````
118 |
119 | A sequence of more than three hyphens is
120 | parsed as a sequence of em and/or en dashes,
121 | with no hyphens. If possible, a homogeneous
122 | sequence of dashes is used (so, 10 hyphens
123 | = 5 en dashes, and 9 hyphens = 3 em dashes).
124 | When a heterogeneous sequence must be used,
125 | the em dashes come first, followed by the en
126 | dashes, and as few en dashes as possible are
127 | used (so, 7 hyphens = 2 em dashes an 1 en
128 | dash).
129 |
130 | ```````````````````````````````` example_smartpunct
131 | one-
132 | two--
133 | three---
134 | four----
135 | five-----
136 | six------
137 | seven-------
138 | eight--------
139 | nine---------
140 | thirteen-------------.
141 | .
142 |
160 | ````````````````````````````````
161 |
162 | Three periods form an ellipsis:
163 |
164 | ```````````````````````````````` example_smartpunct
165 | Ellipses...and...and....
166 | .
167 |
Ellipses…and…and….
168 | ````````````````````````````````
169 |
170 | Periods can be escaped if ellipsis-formation
171 | is not wanted:
172 |
173 | ```````````````````````````````` example_smartpunct
174 | No ellipses\.\.\.
175 | .
176 |
No ellipses...
177 | ````````````````````````````````
178 |
--------------------------------------------------------------------------------
/src/source_range.rs:
--------------------------------------------------------------------------------
1 | use super::{cmark_resume_one_event, fmt, Borrow, Error, Event, Options, Range, State};
2 |
3 | /// Serialize a stream of [pulldown-cmark-Events][Event] while preserving the escape characters in `source`.
4 | /// Each input [Event] is accompanied by an optional [Range] that maps it back to the `source` string.
5 | ///
6 | /// Different from [`cmark_resume_with_options`](super::cmark_resume_with_options), which always escape
7 | /// Markdown special characters like `#` or `[`, this function only escapes a special character if
8 | /// it is escaped in `source`.
9 | ///
10 | /// 1. **source**
11 | /// * Markdown source from which `event_and_ranges` are created.
12 | /// 1. **event_and_ranges**
13 | /// * An iterator over [`Event`]-range pairs, for example as returned by [`pulldown_cmark::OffsetIter`].
14 | /// Must match what's provided in `source`.
15 | /// 1. **formatter**
16 | /// * A format writer, can be a `String`.
17 | /// 1. **state**
18 | /// * The optional initial state of the serialization, useful when the operation should be resumed.
19 | /// 1. **options**
20 | /// * Customize the appearance of the serialization. All otherwise magic values are contained
21 | /// here.
22 | ///
23 | /// *Returns* the [`State`] of the serialization on success. You can use it as initial state in the
24 | /// next call if you are halting event serialization.
25 | ///
26 | /// *Errors* if the underlying buffer fails (which is unlikely) or if the [`Event`] stream
27 | /// iterated over by `event_and_ranges` cannot ever be produced by deserializing valid Markdown.
28 | /// Each failure mode corresponds to one of [`Error`]'s variants.
29 | pub fn cmark_resume_with_source_range_and_options<'a, I, E, F>(
30 | event_and_ranges: I,
31 | source: &'a str,
32 | mut formatter: F,
33 | state: Option>,
34 | options: Options<'_>,
35 | ) -> Result, Error>
36 | where
37 | I: Iterator>)>,
38 | E: Borrow>,
39 | F: fmt::Write,
40 | {
41 | let mut state = state.unwrap_or_default();
42 | for (event, range) in event_and_ranges {
43 | let update_event_end_index = !matches!(*event.borrow(), Event::Start(_));
44 | let prevent_escape_leading_special_characters = match (&range, event.borrow()) {
45 | // Headers and tables can have special characters that aren't at the start
46 | // of the line, because headers end with `#` and tables have pipes in the middle.
47 | _ if state.current_heading.is_some() || !state.table_alignments.is_empty() => false,
48 | // IMPORTANT: Any changes that allow anything other than `Text`
49 | // breaks the assumption below.
50 | (Some(range), Event::Text(_)) => {
51 | range.start <= state.last_event_end_index ||
52 | // Some source characters are not captured,
53 | // so check the previous character.
54 | source.as_bytes().get(range.start.saturating_sub(1)) != Some(&b'\\')
55 | }
56 | _ => false,
57 | } && !state.is_in_code_block();
58 | if prevent_escape_leading_special_characters {
59 | // Hack to not escape leading special characters.
60 | state.code_block = Some(crate::CodeBlockKind::Fenced);
61 | }
62 | cmark_resume_one_event(event, &mut formatter, &mut state, &options)?;
63 | if prevent_escape_leading_special_characters {
64 | // Assumption: this case only happens when `event` is `Text`,
65 | // so `state.is_in_code_block` should not be changed to `true`.
66 | // Also, `state.is_in_code_block` was `false`.
67 | state.code_block = None;
68 | }
69 |
70 | if let (true, Some(range)) = (update_event_end_index, range) {
71 | state.last_event_end_index = range.end;
72 | }
73 | }
74 | Ok(state)
75 | }
76 |
77 | /// As [`cmark_resume_with_source_range_and_options`], but with default [`Options`].
78 | pub fn cmark_resume_with_source_range<'a, I, E, F>(
79 | event_and_ranges: I,
80 | source: &'a str,
81 | formatter: F,
82 | state: Option>,
83 | ) -> Result, Error>
84 | where
85 | I: Iterator>)>,
86 | E: Borrow>,
87 | F: fmt::Write,
88 | {
89 | cmark_resume_with_source_range_and_options(event_and_ranges, source, formatter, state, Options::default())
90 | }
91 |
92 | /// As [`cmark_resume_with_source_range_and_options`], but with the [`State`] finalized.
93 | pub fn cmark_with_source_range_and_options<'a, I, E, F>(
94 | event_and_ranges: I,
95 | source: &'a str,
96 | mut formatter: F,
97 | options: Options<'_>,
98 | ) -> Result, Error>
99 | where
100 | I: Iterator>)>,
101 | E: Borrow>,
102 | F: fmt::Write,
103 | {
104 | let state = cmark_resume_with_source_range_and_options(
105 | event_and_ranges,
106 | source,
107 | &mut formatter,
108 | Default::default(),
109 | options,
110 | )?;
111 | state.finalize(formatter)
112 | }
113 |
114 | /// As [`cmark_with_source_range_and_options`], but with default [`Options`].
115 | pub fn cmark_with_source_range<'a, I, E, F>(
116 | event_and_ranges: I,
117 | source: &'a str,
118 | mut formatter: F,
119 | ) -> Result, Error>
120 | where
121 | I: Iterator>)>,
122 | E: Borrow>,
123 | F: fmt::Write,
124 | {
125 | cmark_with_source_range_and_options(event_and_ranges, source, &mut formatter, Default::default())
126 | }
127 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-output:
--------------------------------------------------------------------------------
1 | # CommonMark sample document
2 |
3 | ## Basic inline formatting
4 |
5 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam **nonumy
6 | eirmod tempor invidunt** ut labore et *dolore magna aliquyam erat*, sed diam
7 | voluptua. `At vero eos et` accusam et
8 |
9 | ## Headers
10 |
11 | ### Level 3
12 |
13 | #### Level 4
14 |
15 | ##### Level 5
16 |
17 | ###### Level 6
18 |
19 | ## [Links]
20 |
21 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
22 | tempor invidunt ut labore et dolore magna aliquyam erat
23 | (), sed [`diam`] [`voluptua`].
24 |
25 | Lorem ipsum dolor sit amet, [consetetur
26 | sadipscing](http://www.example.com/inline) elitr, sed diam nonumy eirmod tempor
27 | invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos
28 | et accusam et [justo duo dolores][1] et ea rebum. Stet clita kasd gubergren, no
29 | sea [takimata sanctus](./showcase.md) est Lorem ipsum dolor sit amet.
30 |
31 | * [bacon](<(>)
32 | * [bacon](<)>)
33 | * [bacon](test())
34 |
35 | Ask to .
36 |
37 | ## [Images]
38 |
39 | Images as blocks:
40 |
41 | 
42 |
43 | ## Lists
44 |
45 | Unordered lists:
46 |
47 | * Lorem impsum
48 | * Nested
49 | * Inline
50 | * Text
51 | * dolor sit amet
52 | * Nested
53 |
54 | * With Paragraphs and nested blocks:
55 |
56 | >
57 | > A quote
58 |
59 | And some text at the end
60 |
61 | * consetetur sadipscing elitr
62 |
63 | Ordered lists:
64 |
65 | 1. Lorem impsum
66 | 1. Nested
67 | 1. Inline
68 | 1. Text
69 | 1. dolor sit amet
70 | 1. Nested
71 |
72 | 1. With
73 | Paragraphs and nested blocks:
74 |
75 | >
76 | > A quote
77 |
78 | And some text at the end
79 |
80 | 1. consetetur sadipscing elitr
81 |
82 | And a mix of both:
83 |
84 | * Lorem impsum
85 | 1. Nested
86 | 1. Inline
87 | * With
88 | * Some
89 | * Nested
90 | * Bullets
91 | 1. Text
92 | * dolor sit amet
93 |
94 | ## Block level elements
95 |
96 | Block quotes
97 |
98 | >
99 | > Lorem ipsum dolor sit amet, *consetetur sadipscing elitr*, sed diam nonumy
100 | > eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
101 | > voluptua.
102 | >
103 | > Lorem ipsum dolor sit amet, **consetetur sadipscing elitr**, sed diam nonumy
104 | > eirmod tempor invidunt ut `labore et dolore magna` aliquyam erat, sed diam
105 | > voluptua.
106 |
107 | > [!NOTE]
108 | > Highlights information that users should take into account, even when skimming.
109 |
110 | > [!TIP]
111 | > Optional information to help a user be more successful.
112 |
113 | > [!IMPORTANT]
114 | > Crucial information necessary for users to succeed.
115 |
116 | > [!WARNING]
117 | > Critical content demanding immediate user attention due to potential risks.
118 |
119 | > [!CAUTION]
120 | > Negative potential consequences of an action.
121 |
122 | Before we continue, have a ruler:
123 |
124 | ---
125 |
126 | Code blocks without syntax highlighting:
127 |
128 | ````
129 | Some plain
130 | code block
131 | fooo
132 | ````
133 |
134 | Or with syntax highlighting, eg, Rust:
135 |
136 | ````rust
137 | fn main() {
138 | println!("Hello world")
139 | }
140 | ````
141 |
142 | Or Haskell:
143 |
144 | ````haskell
145 | main :: IO ()
146 | main = putStrLn "Hello World"
147 | ````
148 |
149 | Or Scala:
150 |
151 | ````scala
152 | object HelloWorld {
153 | def main(args: Array[String]): Unit = {
154 | println("Hello, world!")
155 | }
156 | }
157 | ````
158 |
159 | Or raw codeblocks:
160 |
161 | ````
162 | ```bash
163 | echo 'hi from the innner codeblock'
164 | ```
165 | ````
166 |
167 | ## HTML
168 |
169 | We can have block html:
170 |
171 |
172 |
173 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
174 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
175 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
176 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
177 |
170 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
171 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
172 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
173 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
174 |
173 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
174 | tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
175 | vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
176 | no sea takimata sanctus est Lorem ipsum dolor sit amet.
177 |
");
366 | }
367 | #[test]
368 | fn text() {
369 | assert_eq!(s(Event::Text("asdf".into())), "asdf");
370 | }
371 | #[test]
372 | fn footnote_reference() {
373 | assert_eq!(s(Event::FootnoteReference("asdf".into())), "[^asdf]");
374 | }
375 | #[test]
376 | fn math() {
377 | assert_eq!(
378 | s(Event::InlineMath(r"\sqrt{3x-1}+(1+x)^2".into())),
379 | r"$\sqrt{3x-1}+(1+x)^2$"
380 | );
381 | assert_eq!(s(Event::InlineMath(r"\sqrt{\$4}".into())), r"$\sqrt{\$4}$");
382 | assert_eq!(s(
383 | Event::DisplayMath(
384 | r"\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)".into()
385 | )),
386 | r"$$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$"
387 | );
388 | }
389 |
--------------------------------------------------------------------------------
/tests/fixtures/snapshots/stupicat-math-output:
--------------------------------------------------------------------------------
1 |
2 |
3 | # `$`-delimited LaTeX Math in pulldown-cmark
4 |
5 | Mathematical expressions extension. Syntax based on
6 | .
7 |
8 | Inline mode mathematical expressions:
9 |
10 | This sentence uses `$` delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$
11 | $\sum_{k=1}^n a_k b_k$: Mathematical expression at head of line
12 |
13 | Display mode mathematical expressions:
14 |
15 | **The Cauchy-Schwarz Inequality**
16 |
17 | $$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$
18 |
19 | Inline math expressions cannot be empty, but display mode expressions can.
20 |
21 | Oops empty $$ expression.
22 |
23 | $$$$
24 |
25 | This is a greedy, left-to-right parser.
26 |
27 | $x$$$$$$$y$$
28 |
29 | $x$$$$$$y$$
30 |
31 | $$x$$$$$$y$$
32 |
33 | Math expressions pass their content through as-is, ignoring any other inline
34 | Markdown constructs:
35 |
36 | $ac$
37 |
38 | $${a*b*c} _c_ d$$
39 |
40 | $not `code`$
41 |
42 | $$
43 |
44 | $$
45 |
46 | $α$
47 |
48 | Sole `$` characters without a matching pair in the same block element
49 | are handled as normal text.
50 |
51 | Hello $world.
52 |
53 | Dollar at end of line$
54 |
55 | Mathematical expressions can continue across multiple lines:
56 |
57 | $5x + 2 =
58 | 17$
59 |
60 | $$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right)
61 | \left( \sum_{k=1}^n b_k^2 \right)$$
62 |
63 | Markdown hard breaks are also not recognized inside math expressions:
64 |
65 | $not a\
66 | hard break
67 | either$
68 |
69 | `$` character can be escaped with backslash in mathematical expressions:
70 |
71 | $\$$
72 |
73 | $$y = \$ x$$
74 |
75 | Inline mode math expressions cannot contain unescaped `$` characters.
76 | Neither can display math.
77 |
78 | $x $ x$
79 |
80 | $$ $ $$
81 |
82 | alpha$$beta$gamma$$delta
83 |
84 | Inline math expressions cannot start or end with whitespace, including newlines:
85 |
86 | these are not math texts: $ y=x$, $y=x $, $
87 | y=x$ and $y=x
88 | $
89 |
90 | >
91 | > The start of a line counts as whitespace $2 +
92 | > $
93 |
94 | While displays can start with whitespace, {${
95 | they should not allow inlines to do that $$2 +
96 | $*$
97 |
98 | Inline math expressions do not need to be surrounded with whitespace:
99 |
100 | these are math texts: foo$y=x$bar and $y=x$bar and foo$y=x$ bar
101 |
102 | Inline math expressions can be surrounded by punctuation:
103 |
104 | math texts: $x=y$! and $x=y$? and $x=y$: and $x=y$. and $x=y$"
105 |
106 | also math texts: !$x=y$! and ?$x=y$? and :$x=y$: and .$x=y$. and "$x=y$"
107 |
108 | braces: ($x=y$) \[$x=y$\] {$x=y$}
109 |
110 | Math expression as only item on a line:
111 |
112 | $x=y$
113 |
114 | Math expressions can be immediately followed by other math expressions:
115 |
116 | $a$$b$
117 |
118 | $a$$$b$$
119 |
120 | $$a$$$b$
121 |
122 | $$a$$$$b$$
123 |
124 | Both inline and display mode math expressions are inline elements with the same
125 | precedence as code spans. The leftmost valid element takes priority:
126 |
127 | $Inline `first$ then\` code
128 |
129 | `Code $first` then$ inline
130 |
131 | $$ Display `first $$ then\` code
132 |
133 | `Code $$ first` then $$ display
134 |
135 | Indicators of block structure take precedence over math expressions:
136 |
137 | $x + y - z$
138 |
139 | $x + y
140 |
141 | * z$
142 |
143 | $$ x + y
144 |
145 | >
146 | > z $$
147 |
148 | This also means that math expressions cannot contain empty lines, since they
149 | start a new paragraph:
150 |
151 | $not
152 |
153 | math$
154 |
155 | $$
156 | not
157 |
158 | math
159 | $$
160 |
161 | It also implies that math notation has lower
162 | parsing power than block elements.
163 |
164 | * $not
165 | *
166 | *
167 | math$
168 |
169 | Note that math can contain embedded math. In scanning
170 | for a closing delimiter, we skip material in balanced
171 | curly braces:
172 |
173 | This is display math:
174 | $$
175 | \text{Hello $x^2$}
176 | $$
177 | And this is inline math:
178 | $\text{Hello $x$ there!}$
179 |
180 | Math expressions must be nested within balanced curly braces.
181 | Backslash-escaped braces do not count.
182 |
183 | This is not valid math: $}{$
184 |
185 | Neither is this: { $}{$ }
186 |
187 | This is: $\}\{$
188 |
189 | This is: $\}$
190 |
191 | Math environment contains 2+2: $}$2+2$
192 |
193 | Math environment contains y: $x {$ $ } $y$
194 |
195 | Math expressions must contain properly nested braces.
196 |
197 | This is not display math. It is inline math:
198 |
199 | $$\text{first $$ second}$
200 |
201 | $$$\text{first $$ second}$
202 |
203 | This is display math:
204 |
205 | $$\text{first $$ second}$$
206 |
207 | $$$\text{first $$ second}$$
208 |
209 | This is also display math, but (counterintuitively) it's allowed to be empty
210 | and expected to be as short as possible:
211 |
212 | $$$$\\text{first $$ second}$$
213 |
214 | Dollar signs must also be backslash-escaped if they occur within math:
215 |
216 | $\text{\$}$
217 |
218 | $$x$x$$
219 |
220 | ${$^$$
221 |
222 | $}$$$$
223 |
224 | $}$\] $$
225 |
226 | ## Edge case tests comparison with GitHub
227 |
228 | Test cases
229 | https://raw.githubusercontent.com/nschloe/github-math-bugs/db938ff690ab7c534d8195fe4a1a5163c20b1134/README.md
230 |
231 | Inline math wrapped in quotes
232 |
233 | $x$ $`y`$
234 |
235 | Inline and display math in the same list
236 |
237 | * $a$
238 |
239 | ````math
240 | a
241 | ````
242 |
243 | $$
244 | a
245 | $$
246 |
247 | *
248 | ````math
249 | b
250 | ````
251 |
252 | $$
253 | b
254 | $$
255 |
256 | Images and math in the same list
257 |
258 | * 
259 | * $x$
260 |
261 | Inline and display math in ``
262 |
263 |
264 |
265 | $A = 5$
266 |
267 | $$
268 | A = 5
269 | $$
270 |
271 |
272 |
273 | `<` without surrounding whitespace
274 |
275 | $a c$
318 |
319 | $[(a+b)c](d+e)$
320 |
321 | ${a}_b c_{d}$
322 |
323 | Dollar-math with spaces
324 |
325 | When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
326 | $$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
327 |
328 | Spacing around dollar sign in math mode
329 |
330 | $x = \$$
331 |
332 | Math in italic text
333 |
334 | *Equation $\Omega(69)$ in italic text*
335 |
336 | Inline math can't be preceded by brackets, quotation marks etc.
337 |
338 | $\pi$
339 | '$\pi$
340 | "$\pi$
341 | ($\pi$
342 | \[$\pi$
343 | {$\pi$
344 | /$\pi$
345 |
346 | ## Relationship with tables
347 |
348 | As a block element, tables parsing is stronger than math.
349 |
350 | |first $|$ second|
351 | |-------|--------|
352 | |a ${|}$ b|
353 |
354 | As a special case, pipes in math environments in tables are escaped
355 | with backslashes. Though backslash-escaped characters in math
356 | environments are normally passed through verbatim to the LaTeX
357 | engine, escaped pipes in tables are an exception like they
358 | are in code spans.
359 |
360 | The behavior of the table parser should be as-if it found the bounds
361 | of the table cell in a separate pass that only looked for the
362 | strings `|` and `\|`, treating pipes as boundaries and removing the
363 | escaping backslash before passing the string to the inline parser.
364 |
365 | |first $|$ second|
366 | |-------------|
367 | |a ${ | }$ b|
368 |
369 | |Description|Test case|
370 | |-----------|---------|
371 | |Single|$$|
372 | |Double|$\\$|
373 | |Basic test|$|$|
374 | |Basic test 2|$\|\|$|
375 | |Basic test 3|$x\|y\|z$|
376 | |Not pipe|$\.$|
377 | |Combo|$\.|$|
378 | |Combo 2|$.\|$|
379 | |Extra|$\\\.$|
380 | |Wait, what?|$\|$|
381 | |Wait, what?|$\\|$|
382 | |Wait, what?|$\\\|$|
383 | |Wait, what?|$\\\\|$|
384 |
385 | ## Implementation limits
386 |
387 | Implementations may impose limits on brace nesting to avoid performance issues,
388 | but at least three levels of nesting should be supported.
389 |
390 | Pulldown-cmark imposes the following limits:
391 |
392 | 1. At 25 levels of nesting, it switches from tracking nested pairs to simply
393 | counting the number of braces. This means the below example will spurriously
394 | recognize a math environment with the correct number of braces, but not
395 | nested correctly.
396 |
397 | This is not an inline math environment: $}{$
398 | But, because it's nested too deeply, this is parsed as an inline math environment:
399 | {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
400 | improperly $}{$ nested
401 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
402 | But this still isn't, because the braces are still counted: $}{$
403 |
404 | This is also deeply nested, but, unlike the first example,
405 | they don't have an equal number of close braces and open braces,
406 | so aren't detected as math.
407 | {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
408 | improperly $}$ nested ${$ example
409 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
410 | This, however, is detected ${}$
411 |
412 | ${{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
413 | another improperly nested example
414 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}$
415 |
416 | 2. At 255 distinct brace-delimited groups, the counter rolls over. This means
417 | the below example will spurriously recognize an incorrectly-nested
418 | inline math environment.
419 |
420 | ${}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 20 brace pairs
421 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 40 brace pairs
422 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 60 brace pairs
423 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 80 brace pairs
424 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 100 brace pairs
425 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 120 brace pairs
426 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 140 brace pairs
427 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 160 brace pairs
428 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 180 brace pairs
429 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 200 brace pairs
430 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 220 brace pairs
431 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 240 brace pairs
432 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{$ 255 brace pairs and one unclosed brace
433 |
434 | 3. Thanks to rule 1, though, deeply-nested structures won't chew through all of
435 | the ID space. This means that the below example, even though it nests 255
436 | levels deep, parses correctly anyway.
437 |
438 | ${{{{{{{{{{{{{{{{{{{{ 20 open braces
439 | {{{{{{{{{{{{{{{{{{{{ 40 open braces
440 | {{{{{{{{{{{{{{{{{{{{ 60 open braces
441 | {{{{{{{{{{{{{{{{{{{{ 80 open braces
442 | {{{{{{{{{{{{{{{{{{{{ 100 open braces
443 | {{{{{{{{{{{{{{{{{{{{ 110 open braces
444 | {{{{{{{{{{{{{{{{{{{{ 120 open braces
445 | {{{{{{{{{{{{{{{{{{{{ 140 open braces
446 | {{{{{{{{{{{{{{{{{{{{ 160 open braces
447 | {{{{{{{{{{{{{{{{{{{{ 180 open braces
448 | {{{{{{{{{{{{{{{{{{{{ 200 open braces
449 | {{{{{{{{{{{{{{{{{{{{ 220 open braces
450 | {{{{{{{{{{{{{{{{{{{{ 240 open braces
451 | {{{{{{{{{{{{{{{ 255 open braces
452 | }}}}}}}}}}}}}}}}}}}} 20 close braces
453 | }}}}}}}}}}}}}}}}}}}} 40 close braces
454 | }}}}}}}}}}}}}}}}}}}} 60 close braces
455 | }}}}}}}}}}}}}}}}}}}} 80 close braces
456 | }}}}}}}}}}}}}}}}}}}} 100 close braces
457 | }}}}}}}}}}}}}}}}}}}} 120 close braces
458 | }}}}}}}}}}}}}}}}}}}} 140 close braces
459 | }}}}}}}}}}}}}}}}}}}} 160 close braces
460 | }}}}}}}}}}}}}}}}}}}} 180 close braces
461 | }}}}}}}}}}}}}}}}}}}} 200 close braces
462 | }}}}}}}}}}}}}}}}}}}} 220 close braces
463 | }}}}}}}}}}}}}}}}}}}} 240 close braces
464 | }}}}}}}}}}}}}}}{$ 255 close braces and one open brace
465 |
466 | [^a]: Lorem $a$
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2018 "Sebastian Thiel ", "Dylan Owen ", "Alessandro Ogier ", "Zixian Cai <2891235+caizixian@users.noreply.github.com>"
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/tests/fixtures/math.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # `$`-delimited LaTeX Math in pulldown-cmark
4 |
5 | Mathematical expressions extension. Syntax based on
6 | .
7 |
8 | Inline mode mathematical expressions:
9 |
10 | This sentence uses `$` delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$
11 | $\sum_{k=1}^n a_k b_k$: Mathematical expression at head of line
12 |
13 | Display mode mathematical expressions:
14 |
15 | **The Cauchy-Schwarz Inequality**
16 |
17 | $$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$
18 |
19 | Inline math expressions cannot be empty, but display mode expressions can.
20 |
21 | Oops empty $$ expression.
22 |
23 | $$$$
24 |
25 | This is a greedy, left-to-right parser.
26 |
27 | $x$$$$$$$y$$
28 |
29 | $x$$$$$$y$$
30 |
31 | $$x$$$$$$y$$
32 |
33 | Math expressions pass their content through as-is, ignoring any other inline
34 | Markdown constructs:
35 |
36 | $ac$
37 |
38 | $${a*b*c} _c_ d$$
39 |
40 | $not `code`$
41 |
42 | $$
43 |
44 | $$
45 |
46 | $α$
47 |
48 | Sole `$` characters without a matching pair in the same block element
49 | are handled as normal text.
50 |
51 | Hello $world.
52 |
53 | Dollar at end of line$
54 |
55 | Mathematical expressions can continue across multiple lines:
56 |
57 | $5x + 2 =
58 | 17$
59 |
60 | $$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right)
61 | \left( \sum_{k=1}^n b_k^2 \right)$$
62 |
63 | Markdown hard breaks are also not recognized inside math expressions:
64 |
65 | $not a\
66 | hard break
67 | either$
68 |
69 | `$` character can be escaped with backslash in mathematical expressions:
70 |
71 | $\$$
72 |
73 | $$y = \$ x$$
74 |
75 | Inline mode math expressions cannot contain unescaped `$` characters.
76 | Neither can display math.
77 |
78 | $x $ x$
79 |
80 | $$ $ $$
81 |
82 | alpha$$beta$gamma$$delta
83 |
84 | Inline math expressions cannot start or end with whitespace, including newlines:
85 |
86 | these are not math texts: $ y=x$, $y=x $, $
87 | y=x$ and $y=x
88 | $
89 |
90 | >The start of a line counts as whitespace $2 +
91 | >$
92 |
93 | While displays can start with whitespace, {${
94 | they should not allow inlines to do that $$2 +
95 | $*$
96 |
97 | Inline math expressions do not need to be surrounded with whitespace:
98 |
99 | these are math texts: foo$y=x$bar and $y=x$bar and foo$y=x$ bar
100 |
101 | Inline math expressions can be surrounded by punctuation:
102 |
103 | math texts: $x=y$! and $x=y$? and $x=y$: and $x=y$. and $x=y$"
104 |
105 | also math texts: !$x=y$! and ?$x=y$? and :$x=y$: and .$x=y$. and "$x=y$"
106 |
107 | braces: ($x=y$) [$x=y$] {$x=y$}
108 |
109 | Math expression as only item on a line:
110 |
111 | $x=y$
112 |
113 | Math expressions can be immediately followed by other math expressions:
114 |
115 | $a$$b$
116 |
117 | $a$$$b$$
118 |
119 | $$a$$$b$
120 |
121 | $$a$$$$b$$
122 |
123 | Both inline and display mode math expressions are inline elements with the same
124 | precedence as code spans. The leftmost valid element takes priority:
125 |
126 | $Inline `first$ then` code
127 |
128 | `Code $first` then$ inline
129 |
130 | $$ Display `first $$ then` code
131 |
132 | `Code $$ first` then $$ display
133 |
134 | Indicators of block structure take precedence over math expressions:
135 |
136 | $x + y - z$
137 |
138 | $x + y
139 | - z$
140 |
141 | $$ x + y
142 | > z $$
143 |
144 | This also means that math expressions cannot contain empty lines, since they
145 | start a new paragraph:
146 |
147 | $not
148 |
149 | math$
150 |
151 | $$
152 | not
153 |
154 | math
155 | $$
156 |
157 | It also implies that math notation has lower
158 | parsing power than block elements.
159 |
160 | - $not
161 | - *
162 | math$
163 |
164 | Note that math can contain embedded math. In scanning
165 | for a closing delimiter, we skip material in balanced
166 | curly braces:
167 |
168 | This is display math:
169 | $$
170 | \text{Hello $x^2$}
171 | $$
172 | And this is inline math:
173 | $\text{Hello $x$ there!}$
174 |
175 | Math expressions must be nested within balanced curly braces.
176 | Backslash-escaped braces do not count.
177 |
178 | This is not valid math: $}{$
179 |
180 | Neither is this: { $}{$ }
181 |
182 | This is: $\}\{$
183 |
184 | This is: $\}$
185 |
186 | Math environment contains 2+2: $}$2+2$
187 |
188 | Math environment contains y: $x {$ $ } $y$
189 |
190 | Math expressions must contain properly nested braces.
191 |
192 | This is not display math. It is inline math:
193 |
194 | $$\text{first $$ second}$
195 |
196 | $$$\text{first $$ second}$
197 |
198 | This is display math:
199 |
200 | $$\text{first $$ second}$$
201 |
202 | $$$\text{first $$ second}$$
203 |
204 | This is also display math, but (counterintuitively) it's allowed to be empty
205 | and expected to be as short as possible:
206 |
207 | $$$$\text{first $$ second}$$
208 |
209 | Dollar signs must also be backslash-escaped if they occur within math:
210 |
211 | $\text{\$}$
212 |
213 | $$x$x$$
214 |
215 | ${$^$$
216 |
217 | $}$$$$
218 |
219 | $}$] $$
220 |
221 | ## Edge case tests comparison with GitHub
222 |
223 | Test cases
224 | https://raw.githubusercontent.com/nschloe/github-math-bugs/db938ff690ab7c534d8195fe4a1a5163c20b1134/README.md
225 |
226 | Inline math wrapped in quotes
227 |
228 | $x$ $`y`$
229 |
230 | Inline and display math in the same list
231 |
232 | - $a$
233 |
234 | ```math
235 | a
236 | ```
237 |
238 | $$
239 | a
240 | $$
241 |
242 | - ```math
243 | b
244 | ```
245 |
246 | $$
247 | b
248 | $$
249 |
250 | Images and math in the same list
251 |
252 | - 
253 | - $x$
254 |
255 | Inline and display math in ``
256 |
257 |
258 |
259 | $A = 5$
260 |
261 | $$
262 | A = 5
263 | $$
264 |
265 |
266 |
267 | `<` without surrounding whitespace
268 |
269 | $a c$
312 |
313 | $[(a+b)c](d+e)$
314 |
315 | ${a}_b c_{d}$
316 |
317 | Dollar-math with spaces
318 |
319 | When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
320 | $$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
321 |
322 | Spacing around dollar sign in math mode
323 |
324 | $x = \$$
325 |
326 | Math in italic text
327 |
328 | _Equation $\Omega(69)$ in italic text_
329 |
330 | Inline math can't be preceded by brackets, quotation marks etc.
331 |
332 | $\pi$
333 | '$\pi$
334 | "$\pi$
335 | ($\pi$
336 | [$\pi$
337 | {$\pi$
338 | /$\pi$
339 |
340 | ## Relationship with tables
341 |
342 | As a block element, tables parsing is stronger than math.
343 |
344 | | first $|$ second |
345 | |--------|---------|
346 | | a ${ | }$ b |
347 |
348 | As a special case, pipes in math environments in tables are escaped
349 | with backslashes. Though backslash-escaped characters in math
350 | environments are normally passed through verbatim to the LaTeX
351 | engine, escaped pipes in tables are an exception like they
352 | are in code spans.
353 |
354 | The behavior of the table parser should be as-if it found the bounds
355 | of the table cell in a separate pass that only looked for the
356 | strings `|` and `\|`, treating pipes as boundaries and removing the
357 | escaping backslash before passing the string to the inline parser.
358 |
359 | | first $\|$ second |
360 | |-------------------|
361 | | a ${ \| }$ b |
362 |
363 | | Description | Test case |
364 | |-------------|-----------|
365 | | Single | $\$ |
366 | | Double | $\\$ |
367 | | Basic test | $\|$ |
368 | | Basic test 2| $\|\|\$ |
369 | | Basic test 3| $x\|y\|z\$|
370 | | Not pipe | $\.$ |
371 | | Combo | $\.\|$ |
372 | | Combo 2 | $\.\|\$ |
373 | | Extra | $\\\.$ |
374 | | Wait, what? | $\\|$ |
375 | | Wait, what? | $\\\|$ |
376 | | Wait, what? | $\\\\|$ |
377 | | Wait, what? | $\\\\\|$ |
378 |
379 | ## Implementation limits
380 |
381 | Implementations may impose limits on brace nesting to avoid performance issues,
382 | but at least three levels of nesting should be supported.
383 |
384 | Pulldown-cmark imposes the following limits:
385 |
386 | 1. At 25 levels of nesting, it switches from tracking nested pairs to simply
387 | counting the number of braces. This means the below example will spurriously
388 | recognize a math environment with the correct number of braces, but not
389 | nested correctly.
390 |
391 | This is not an inline math environment: $}{$
392 | But, because it's nested too deeply, this is parsed as an inline math environment:
393 | {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
394 | improperly $}{$ nested
395 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
396 | But this still isn't, because the braces are still counted: $}{$
397 |
398 | This is also deeply nested, but, unlike the first example,
399 | they don't have an equal number of close braces and open braces,
400 | so aren't detected as math.
401 | {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
402 | improperly $}$ nested ${$ example
403 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
404 | This, however, is detected ${}$
405 |
406 | ${{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
407 | another improperly nested example
408 | }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}$
409 |
410 | 2. At 255 distinct brace-delimited groups, the counter rolls over. This means
411 | the below example will spurriously recognize an incorrectly-nested
412 | inline math environment.
413 |
414 | ${}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 20 brace pairs
415 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 40 brace pairs
416 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 60 brace pairs
417 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 80 brace pairs
418 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 100 brace pairs
419 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 120 brace pairs
420 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 140 brace pairs
421 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 160 brace pairs
422 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 180 brace pairs
423 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 200 brace pairs
424 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 220 brace pairs
425 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{} 240 brace pairs
426 | {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{$ 255 brace pairs and one unclosed brace
427 |
428 | 3. Thanks to rule 1, though, deeply-nested structures won't chew through all of
429 | the ID space. This means that the below example, even though it nests 255
430 | levels deep, parses correctly anyway.
431 |
432 | ${{{{{{{{{{{{{{{{{{{{ 20 open braces
433 | {{{{{{{{{{{{{{{{{{{{ 40 open braces
434 | {{{{{{{{{{{{{{{{{{{{ 60 open braces
435 | {{{{{{{{{{{{{{{{{{{{ 80 open braces
436 | {{{{{{{{{{{{{{{{{{{{ 100 open braces
437 | {{{{{{{{{{{{{{{{{{{{ 110 open braces
438 | {{{{{{{{{{{{{{{{{{{{ 120 open braces
439 | {{{{{{{{{{{{{{{{{{{{ 140 open braces
440 | {{{{{{{{{{{{{{{{{{{{ 160 open braces
441 | {{{{{{{{{{{{{{{{{{{{ 180 open braces
442 | {{{{{{{{{{{{{{{{{{{{ 200 open braces
443 | {{{{{{{{{{{{{{{{{{{{ 220 open braces
444 | {{{{{{{{{{{{{{{{{{{{ 240 open braces
445 | {{{{{{{{{{{{{{{ 255 open braces
446 | }}}}}}}}}}}}}}}}}}}} 20 close braces
447 | }}}}}}}}}}}}}}}}}}}} 40 close braces
448 | }}}}}}}}}}}}}}}}}}}} 60 close braces
449 | }}}}}}}}}}}}}}}}}}}} 80 close braces
450 | }}}}}}}}}}}}}}}}}}}} 100 close braces
451 | }}}}}}}}}}}}}}}}}}}} 120 close braces
452 | }}}}}}}}}}}}}}}}}}}} 140 close braces
453 | }}}}}}}}}}}}}}}}}}}} 160 close braces
454 | }}}}}}}}}}}}}}}}}}}} 180 close braces
455 | }}}}}}}}}}}}}}}}}}}} 200 close braces
456 | }}}}}}}}}}}}}}}}}}}} 220 close braces
457 | }}}}}}}}}}}}}}}}}}}} 240 close braces
458 | }}}}}}}}}}}}}}}{$ 255 close braces and one open brace
459 |
460 | [^a]: Lorem $a$
461 |
--------------------------------------------------------------------------------
/tests/spec/CommonMark/LICENSE:
--------------------------------------------------------------------------------
1 | The CommonMark spec (spec.txt) and DTD (CommonMark.dtd) are
2 |
3 | Copyright (C) 2014-16 John MacFarlane
4 |
5 | Released under the Creative Commons CC-BY-SA 4.0 license:
6 | .
7 |
8 | Creative Commons Attribution-ShareAlike 4.0 International Public License
9 |
10 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
11 |
12 | Section 1 – Definitions.
13 |
14 | Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
15 | Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
16 | BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.
17 | Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
18 | Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
19 | Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
20 | License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike.
21 | Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
22 | Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
23 | Licensor means the individual(s) or entity(ies) granting rights under this Public License.
24 | Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
25 | Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
26 | You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
27 | Section 2 – Scope.
28 |
29 | License grant.
30 | Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
31 | reproduce and Share the Licensed Material, in whole or in part; and
32 | produce, reproduce, and Share Adapted Material.
33 | Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
34 | Term. The term of this Public License is specified in Section 6(a).
35 | Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
36 | Downstream recipients.
37 | Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
38 | Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
39 | No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
40 | No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
41 | Other rights.
42 |
43 | Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
44 | Patent and trademark rights are not licensed under this Public License.
45 | To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
46 | Section 3 – License Conditions.
47 |
48 | Your exercise of the Licensed Rights is expressly made subject to the following conditions.
49 |
50 | Attribution.
51 |
52 | If You Share the Licensed Material (including in modified form), You must:
53 |
54 | retain the following if it is supplied by the Licensor with the Licensed Material:
55 | identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
56 | a copyright notice;
57 | a notice that refers to this Public License;
58 | a notice that refers to the disclaimer of warranties;
59 | a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
60 | indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
61 | indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
62 | You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
63 | If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
64 | ShareAlike.
65 | In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
66 |
67 | The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License.
68 | You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
69 | You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
70 | Section 4 – Sui Generis Database Rights.
71 |
72 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
73 |
74 | for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
75 | if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
76 | You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
77 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
78 | Section 5 – Disclaimer of Warranties and Limitation of Liability.
79 |
80 | Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
81 | To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
82 | The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
83 | Section 6 – Term and Termination.
84 |
85 | This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
86 | Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
87 |
88 | automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
89 | upon express reinstatement by the Licensor.
90 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
91 | For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
92 | Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
93 | Section 7 – Other Terms and Conditions.
94 |
95 | The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
96 | Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
97 | Section 8 – Interpretation.
98 |
99 | For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
100 | To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
101 | No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
102 | Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
103 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
104 |
105 | Creative Commons may be contacted at creativecommons.org.
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(rust_2018_idioms)]
2 |
3 | use std::{
4 | borrow::{Borrow, Cow},
5 | collections::HashSet,
6 | fmt,
7 | ops::Range,
8 | };
9 |
10 | use pulldown_cmark::{Alignment as TableAlignment, BlockQuoteKind, Event, LinkType, MetadataBlockKind, Tag, TagEnd};
11 |
12 | mod source_range;
13 | mod text_modifications;
14 |
15 | pub use source_range::{
16 | cmark_resume_with_source_range, cmark_resume_with_source_range_and_options, cmark_with_source_range,
17 | cmark_with_source_range_and_options,
18 | };
19 | use text_modifications::*;
20 |
21 | /// Similar to [Pulldown-Cmark-Alignment][Alignment], but with required
22 | /// traits for comparison to allow testing.
23 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24 | pub enum Alignment {
25 | None,
26 | Left,
27 | Center,
28 | Right,
29 | }
30 |
31 | impl<'a> From<&'a TableAlignment> for Alignment {
32 | fn from(s: &'a TableAlignment) -> Self {
33 | match *s {
34 | TableAlignment::None => Self::None,
35 | TableAlignment::Left => Self::Left,
36 | TableAlignment::Center => Self::Center,
37 | TableAlignment::Right => Self::Right,
38 | }
39 | }
40 | }
41 |
42 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
43 | pub enum CodeBlockKind {
44 | Indented,
45 | Fenced,
46 | }
47 |
48 | /// The state of the [`cmark_resume()`] and [`cmark_resume_with_options()`] functions.
49 | /// This does not only allow introspection, but enables the user
50 | /// to halt the serialization at any time, and resume it later.
51 | #[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52 | #[non_exhaustive]
53 | pub struct State<'a> {
54 | /// The amount of newlines to insert after `Event::Start(...)`
55 | pub newlines_before_start: usize,
56 | /// The lists and their types for which we have seen a `Event::Start(List(...))` tag
57 | pub list_stack: Vec